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