QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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;
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  mWidget->setStyleSheet( QString(
112  ".QWidget{"
113  "border: 1px solid %1;"
114  "background-color: %2;}" ).arg(
115  strokeColor, backgroundColor ) );
116 
117  tipText = fetchFeature( pLayer, mapPosition, pMapCanvas );
118 
119  mMapTipVisible = !tipText.isEmpty();
120  if ( !mMapTipVisible )
121  {
122  clear();
123  return;
124  }
125 
126  if ( tipText == lastTipText )
127  {
128  return;
129  }
130 
131  bodyStyle = QString(
132  "background-color: %1;"
133  "margin: 0;"
134  "font: %2pt \"%3\";" ).arg( backgroundColor ).arg( mFontSize ).arg( mFontFamily );
135 
136  containerStyle = QString(
137  "display: inline-block;"
138  "margin: 0px" );
139 
140  tipHtml = QString(
141  "<html>"
142  "<body style='%1'>"
143  "<div id='QgsWebViewContainer' style='%2'>%3</div>"
144  "</body>"
145  "</html>" ).arg( bodyStyle, containerStyle, tipText );
146 
147  QgsDebugMsg( tipHtml );
148 
149  mWidget->move( pixelPosition.x(),
150  pixelPosition.y() );
151 
152  mWebView->setHtml( tipHtml );
153  lastTipText = tipText;
154 
155  mWidget->show();
156 }
157 
158 void QgsMapTip::resizeContent()
159 {
160 #if WITH_QTWEBKIT
161  // Get the content size
162  QWebElement container = mWebView->page()->mainFrame()->findFirstElement(
163  QStringLiteral( "#QgsWebViewContainer" ) );
164  int width = container.geometry().width() + MARGIN_VALUE * 2;
165  int height = container.geometry().height() + MARGIN_VALUE * 2;
166  mWidget->resize( width, height );
167 #else
168  mWebView->adjustSize();
169 #endif
170 }
171 
173 {
174  if ( !mMapTipVisible )
175  return;
176 
177  mWebView->setHtml( QString() );
178  mWidget->hide();
179 
180  // Reset the visible flag
181  mMapTipVisible = false;
182 }
183 
184 QString QgsMapTip::fetchFeature( QgsMapLayer *layer, QgsPointXY &mapPosition, QgsMapCanvas *mapCanvas )
185 {
186  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
187  if ( !vlayer )
188  return QString();
189 
190  double searchRadius = QgsMapTool::searchRadiusMU( mapCanvas );
191 
192  QgsRectangle r;
193  r.setXMinimum( mapPosition.x() - searchRadius );
194  r.setYMinimum( mapPosition.y() - searchRadius );
195  r.setXMaximum( mapPosition.x() + searchRadius );
196  r.setYMaximum( mapPosition.y() + searchRadius );
197 
198  r = mapCanvas->mapSettings().mapToLayerCoordinates( layer, r );
199 
201  if ( mapCanvas )
203 
204  QString mapTip = vlayer->mapTipTemplate();
205  QString tipString;
206  QgsExpression exp( vlayer->displayExpression() );
207  QgsFeature feature;
209  if ( mapTip.isEmpty() )
210  {
211  exp.prepare( &context );
212  request.setSubsetOfAttributes( exp.referencedColumns(), vlayer->fields() );
213  }
214  QgsFeatureIterator it = vlayer->getFeatures( request );
215  QTime timer;
216  timer.start();
217  while ( it.nextFeature( feature ) )
218  {
219  context.setFeature( feature );
220  if ( !mapTip.isEmpty() )
221  {
222  tipString = QgsExpression::replaceExpressionText( mapTip, &context );
223  }
224  else
225  {
226  tipString = exp.evaluate( &context ).toString();
227  }
228 
229  if ( !tipString.isEmpty() || timer.elapsed() >= 1000 )
230  {
231  break;
232  }
233  }
234 
235  return tipString;
236 }
237 
239 {
240  QgsSettings settings;
241  QFont defaultFont = qApp->font();
242  mFontSize = settings.value( QStringLiteral( "/qgis/stylesheet/fontPointSize" ), defaultFont.pointSize() ).toInt();
243  mFontFamily = settings.value( QStringLiteral( "/qgis/stylesheet/fontFamily" ), defaultFont.family() ).toString();
244 }
245 
246 // This slot handles all clicks
247 void QgsMapTip::onLinkClicked( const QUrl &url )
248 {
249  QDesktopServices::openUrl( url );
250 }
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:78
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:58
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:73
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:172
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:238
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...