QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgsmaptip.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaptips.cpp - Query a layer and show a maptip on the canvas
3  ---------------------
4  begin : October 2007
5  copyright : (C) 2007 by Gary Sherman
6  email : sherman @ mrcc 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 // QGIS includes
16 #include "qgsfeatureiterator.h"
17 #include "qgsmapcanvas.h"
18 #include "qgsmaptool.h"
19 #include "qgsvectorlayer.h"
20 #include "qgsexpression.h"
21 #include "qgslogger.h"
22 #include "qgssettings.h"
23 #include "qgswebview.h"
24 #include "qgswebframe.h"
25 #include "qgsapplication.h"
26 #include "qgsrenderer.h"
28 
29 // Qt includes
30 #include <QPoint>
31 #include <QToolTip>
32 #include <QSettings>
33 #include <QLabel>
34 #include <QDesktopServices>
35 #if WITH_QTWEBKIT
36 #include <QWebElement>
37 #endif
38 #include <QHBoxLayout>
39 
40 
41 #include "qgsmaptip.h"
42 
44 {
45  // Init the visible flag
46  mMapTipVisible = false;
47 
48  // Init font-related values
50 }
51 
53  QgsPointXY &mapPosition,
54  QPoint &pixelPosition,
55  QgsMapCanvas *pMapCanvas )
56 {
57  // Do the search using the active layer and the preferred label field for the
58  // layer. The label field must be defined in the layer configuration
59  // file/database. The code required to do this is similar to identify, except
60  // we only want the first qualifying feature and we will only display the
61  // field defined as the label field in the layer configuration file/database
62 
63  // Do not render map tips if the layer is not visible
64  if ( !pMapCanvas->layers().contains( pLayer ) )
65  {
66  return;
67  }
68 
69  // Show the maptip on the canvas
70  QString tipText, lastTipText, tipHtml, bodyStyle, containerStyle,
71  backgroundColor, strokeColor, textColor;
72 
73  delete mWidget;
74  mWidget = new QWidget( pMapCanvas );
75  mWidget->setContentsMargins( MARGIN_VALUE, MARGIN_VALUE, MARGIN_VALUE, MARGIN_VALUE );
76  mWebView = new QgsWebView( mWidget );
77 
78 
79 #if WITH_QTWEBKIT
80  mWebView->page()->setLinkDelegationPolicy( QWebPage::DelegateAllLinks );//Handle link clicks by yourself
81  mWebView->setContextMenuPolicy( Qt::NoContextMenu ); //No context menu is allowed if you don't need it
82  connect( mWebView, &QWebView::linkClicked, this, &QgsMapTip::onLinkClicked );
83  connect( mWebView, &QWebView::loadFinished, this, [ = ]( bool ) { resizeContent(); } );
84 #endif
85 
86  mWebView->page()->settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true );
87  mWebView->page()->settings()->setAttribute( QWebSettings::JavascriptEnabled, true );
88  mWebView->page()->settings()->setAttribute( QWebSettings::LocalStorageEnabled, true );
89 
90  // Disable scrollbars, avoid random resizing issues
91  mWebView->page()->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
92  mWebView->page()->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
93 
94  QHBoxLayout *layout = new QHBoxLayout;
95  layout->setContentsMargins( 0, 0, 0, 0 );
96  layout->addWidget( mWebView );
97 
98  mWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
99  mWidget->setLayout( layout );
100 
101  // Assure the map tip is never larger than half the map canvas
102  const int MAX_WIDTH = pMapCanvas->geometry().width() / 2;
103  const int MAX_HEIGHT = pMapCanvas->geometry().height() / 2;
104  mWidget->setMaximumSize( MAX_WIDTH, MAX_HEIGHT );
105 
106  // Start with 0 size,
107  // The content will automatically make it grow up to MaximumSize
108  mWidget->resize( 0, 0 );
109 
110  backgroundColor = mWidget->palette().base().color().name();
111  strokeColor = mWidget->palette().shadow().color().name();
112  textColor = mWidget->palette().text().color().name();
113  mWidget->setStyleSheet( QString(
114  ".QWidget{"
115  "border: 1px solid %1;"
116  "background-color: %2;}" ).arg(
117  strokeColor, backgroundColor ) );
118 
119  tipText = fetchFeature( pLayer, mapPosition, pMapCanvas );
120 
121  mMapTipVisible = !tipText.isEmpty();
122  if ( !mMapTipVisible )
123  {
124  clear();
125  return;
126  }
127 
128  if ( tipText == lastTipText )
129  {
130  return;
131  }
132 
133  bodyStyle = QString(
134  "background-color: %1;"
135  "margin: 0;"
136  "font: %2pt \"%3\";"
137  "color: %4;" ).arg( backgroundColor ).arg( mFontSize ).arg( mFontFamily ).arg( textColor );
138 
139  containerStyle = QString(
140  "display: inline-block;"
141  "margin: 0px" );
142 
143  tipHtml = QString(
144  "<html>"
145  "<body style='%1'>"
146  "<div id='QgsWebViewContainer' style='%2'>%3</div>"
147  "</body>"
148  "</html>" ).arg( bodyStyle, containerStyle, tipText );
149 
150  QgsDebugMsg( tipHtml );
151 
152  mWidget->move( pixelPosition.x(),
153  pixelPosition.y() );
154 
155  mWebView->setHtml( tipHtml );
156  lastTipText = tipText;
157 
158  mWidget->show();
159 }
160 
161 void QgsMapTip::resizeContent()
162 {
163 #if WITH_QTWEBKIT
164  // Get the content size
165  QWebElement container = mWebView->page()->mainFrame()->findFirstElement(
166  QStringLiteral( "#QgsWebViewContainer" ) );
167  int width = container.geometry().width() + MARGIN_VALUE * 2;
168  int height = container.geometry().height() + MARGIN_VALUE * 2;
169  mWidget->resize( width, height );
170 #else
171  mWebView->adjustSize();
172 #endif
173 }
174 
176 {
177  if ( !mMapTipVisible )
178  return;
179 
180  mWebView->setHtml( QString() );
181  mWidget->hide();
182 
183  // Reset the visible flag
184  mMapTipVisible = false;
185 }
186 
187 QString QgsMapTip::fetchFeature( QgsMapLayer *layer, QgsPointXY &mapPosition, QgsMapCanvas *mapCanvas )
188 {
189  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
190  if ( !vlayer )
191  return QString();
192 
193  double searchRadius = QgsMapTool::searchRadiusMU( mapCanvas );
194 
195  QgsRectangle r;
196  r.setXMinimum( mapPosition.x() - searchRadius );
197  r.setYMinimum( mapPosition.y() - searchRadius );
198  r.setXMaximum( mapPosition.x() + searchRadius );
199  r.setYMaximum( mapPosition.y() + searchRadius );
200 
201  r = mapCanvas->mapSettings().mapToLayerCoordinates( layer, r );
202 
204  context.appendScope( QgsExpressionContextUtils::mapSettingsScope( mapCanvas->mapSettings() ) );
205 
206  QString mapTip = vlayer->mapTipTemplate();
207  QString tipString;
208  QgsExpression exp( vlayer->displayExpression() );
209  QgsFeature feature;
211  if ( mapTip.isEmpty() )
212  {
213  exp.prepare( &context );
214  request.setSubsetOfAttributes( exp.referencedColumns(), vlayer->fields() );
215  }
216 
219 
220  bool filter = false;
221  std::unique_ptr< QgsFeatureRenderer > renderer;
222  if ( vlayer->renderer() )
223  {
224  renderer.reset( vlayer->renderer()->clone() );
225  renderer->startRender( renderCtx, vlayer->fields() );
226  filter = renderer->capabilities() & QgsFeatureRenderer::Filter;
227 
228  const QString filterExpression = renderer->filter( vlayer->fields() );
229  if ( ! filterExpression.isEmpty() )
230  {
231  request.setFilterExpression( filterExpression );
232  }
233  }
234 
235  QgsFeatureIterator it = vlayer->getFeatures( request );
236  QElapsedTimer timer;
237  timer.start();
238  while ( it.nextFeature( feature ) )
239  {
240  context.setFeature( feature );
241 
242  renderCtx.expressionContext().setFeature( feature );
243  if ( filter && renderer && !renderer->willRenderFeature( feature, renderCtx ) )
244  {
245  continue;
246  }
247 
248  if ( !mapTip.isEmpty() )
249  {
250  tipString = QgsExpression::replaceExpressionText( mapTip, &context );
251  }
252  else
253  {
254  tipString = exp.evaluate( &context ).toString();
255  }
256 
257  if ( !tipString.isEmpty() || timer.elapsed() >= 1000 )
258  {
259  break;
260  }
261  }
262 
263  if ( renderer )
264  renderer->stopRender( renderCtx );
265 
266  return tipString;
267 }
268 
270 {
271  QgsSettings settings;
272  QFont defaultFont = qApp->font();
273  mFontSize = settings.value( QStringLiteral( "/qgis/stylesheet/fontPointSize" ), defaultFont.pointSize() ).toInt();
274  mFontFamily = settings.value( QStringLiteral( "/qgis/stylesheet/fontFamily" ), defaultFont.family() ).toString();
275 }
276 
277 // This slot handles all clicks
278 void QgsMapTip::onLinkClicked( const QUrl &url )
279 {
280  QDesktopServices::openUrl( url );
281 }
QgsVectorLayer::getFeatures
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Definition: qgsvectorlayer.cpp:993
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:370
qgsexpressioncontextutils.h
QgsFeatureRenderer::Filter
@ Filter
Features may be filtered, i.e. some features may not be rendered (categorized, rule based ....
Definition: qgsrenderer.h:256
QgsPointXY::y
double y
Definition: qgspointxy.h:48
QgsMapTip::clear
void clear(QgsMapCanvas *mpMapCanvas=nullptr)
Clear the current maptip if it exists.
Definition: qgsmaptip.cpp:175
QgsMapSettings::mapToLayerCoordinates
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer's CRS
Definition: qgsmapsettings.cpp:537
QgsRenderContext::expressionContext
QgsExpressionContext & expressionContext()
Gets the expression context.
Definition: qgsrendercontext.h:596
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:174
QgsFeatureRequest::ExactIntersect
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
Definition: qgsfeaturerequest.h:83
qgsmapcanvas.h
QgsRenderContext::fromMapSettings
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
Definition: qgsrendercontext.cpp:197
qgsexpression.h
qgswebframe.h
QgsMapCanvas::mapSettings
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
Definition: qgsmapcanvas.cpp:391
QgsPointXY::x
Q_GADGET double x
Definition: qgspointxy.h:47
qgsfeatureiterator.h
QgsExpressionContextUtils::layerScope
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Definition: qgsexpressioncontextutils.cpp:265
QgsMapCanvas
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:85
QgsExpressionContextUtils::mapSettingsScope
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
Definition: qgsexpressioncontextutils.cpp:357
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:58
QgsExpressionContextUtils::globalProjectLayerScopes
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Definition: qgsexpressioncontextutils.cpp:307
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QgsFeatureRequest::setSubsetOfAttributes
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
Definition: qgsfeaturerequest.cpp:185
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsMapCanvas::layers
QList< QgsMapLayer * > layers() const
Returns the list of layers shown within the map canvas.
Definition: qgsmapcanvas.cpp:2139
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QgsFeatureRequest::setFilterExpression
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
Definition: qgsfeaturerequest.cpp:124
qgsapplication.h
QgsFeatureRequest::setFilterRect
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
Definition: qgsfeaturerequest.cpp:92
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3283
QgsFeatureRequest
This class wraps a request for features to a vector layer (or directly its vector data provider).
Definition: qgsfeaturerequest.h:76
QgsFeatureRenderer::clone
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
qgsmaptool.h
QgsRectangle::setXMinimum
void setXMinimum(double x) SIP_HOLDGIL
Set the minimum x value.
Definition: qgsrectangle.h:130
qgsrenderer.h
QgsRectangle::setXMaximum
void setXMaximum(double x) SIP_HOLDGIL
Set the maximum x value.
Definition: qgsrectangle.h:135
qgsvectorlayer.h
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:44
QgsRectangle::setYMaximum
void setYMaximum(double y) SIP_HOLDGIL
Set the maximum y value.
Definition: qgsrectangle.h:145
qgsmaptip.h
QgsVectorLayer::mapTipTemplate
QString mapTipTemplate
Definition: qgsvectorlayer.h:392
QgsMapTip::applyFontSettings
void applyFontSettings()
Apply font family and size to match user settings.
Definition: qgsmaptip.cpp:269
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:374
QgsRectangle::setYMinimum
void setYMinimum(double y) SIP_HOLDGIL
Set the minimum y value.
Definition: qgsrectangle.h:140
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:387
QgsMapLayer
Base class for all map layer types.
Definition: qgsmaplayer.h:83
qgssettings.h
QgsMapTool::searchRadiusMU
static double searchRadiusMU(const QgsRenderContext &context)
Gets search radius in map units for given context.
Definition: qgsmaptool.cpp:212
QgsFeature
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
qgswebview.h
qgslogger.h
QgsMapTip::QgsMapTip
QgsMapTip()
Default constructor.
Definition: qgsmaptip.cpp:43
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:105
QgsVectorLayer::displayExpression
QString displayExpression
Definition: qgsvectorlayer.h:391
QgsFeatureIterator
Wrapper for iterator of features from vector data provider or vector layer.
Definition: qgsfeatureiterator.h:265
QgsExpression::replaceExpressionText
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
Definition: qgsexpression.cpp:430
QgsFeatureRequest::setFlags
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
Definition: qgsfeaturerequest.cpp:179
QgsMapTip::showMapTip
void showMapTip(QgsMapLayer *thepLayer, QgsPointXY &mapPosition, QPoint &pixelPosition, QgsMapCanvas *mpMapCanvas)
Show a maptip at a given point on the map canvas.
Definition: qgsmaptip.cpp:52
QgsWebView
The QgsWebView class is a collection of stubs to mimic the API of QWebView on systems where the real ...
Definition: qgswebview.h:66
QgsExpressionContext::setFeature
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Definition: qgsexpressioncontext.cpp:521
QgsVectorLayer::renderer
QgsFeatureRenderer * renderer()
Returns renderer.
Definition: qgsvectorlayer.h:892