QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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  context.appendScope( QgsExpressionContextUtils::mapSettingsScope( mapCanvas->mapSettings() ) );
204 
205  QString mapTip = vlayer->mapTipTemplate();
206  QString tipString;
207  QgsExpression exp( vlayer->displayExpression() );
208  QgsFeature feature;
210  if ( mapTip.isEmpty() )
211  {
212  exp.prepare( &context );
213  request.setSubsetOfAttributes( exp.referencedColumns(), vlayer->fields() );
214  }
215  QgsFeatureIterator it = vlayer->getFeatures( request );
216  QElapsedTimer timer;
217  timer.start();
218  while ( it.nextFeature( feature ) )
219  {
220  context.setFeature( feature );
221  if ( !mapTip.isEmpty() )
222  {
223  tipString = QgsExpression::replaceExpressionText( mapTip, &context );
224  }
225  else
226  {
227  tipString = exp.evaluate( &context ).toString();
228  }
229 
230  if ( !tipString.isEmpty() || timer.elapsed() >= 1000 )
231  {
232  break;
233  }
234  }
235 
236  return tipString;
237 }
238 
240 {
241  QgsSettings settings;
242  QFont defaultFont = qApp->font();
243  mFontSize = settings.value( QStringLiteral( "/qgis/stylesheet/fontPointSize" ), defaultFont.pointSize() ).toInt();
244  mFontFamily = settings.value( QStringLiteral( "/qgis/stylesheet/fontFamily" ), defaultFont.family() ).toString();
245 }
246 
247 // This slot handles all clicks
248 void QgsMapTip::onLinkClicked( const QUrl &url )
249 {
250  QDesktopServices::openUrl( url );
251 }
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:369
qgsexpressioncontextutils.h
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:174
QgsMapSettings::mapToLayerCoordinates
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer's CRS
Definition: qgsmapsettings.cpp:516
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:109
qgsmapcanvas.h
QgsRectangle::setXMinimum
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:130
qgsexpression.h
qgswebframe.h
QgsMapCanvas::mapSettings
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
Definition: qgsmapcanvas.cpp:390
qgsfeatureiterator.h
QgsMapCanvas
Definition: qgsmapcanvas.h:83
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:356
QgsExpressionContextUtils::globalProjectLayerScopes
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Definition: qgsexpressioncontextutils.cpp:306
QgsSettings
Definition: qgssettings.h:61
QgsFeatureRequest::setSubsetOfAttributes
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
Definition: qgsfeaturerequest.cpp:190
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:2103
QgsRectangle
Definition: qgsrectangle.h:41
qgsapplication.h
QgsFeatureRequest::setFilterRect
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
Definition: qgsfeaturerequest.cpp:97
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3280
QgsFeatureRequest
Definition: qgsfeaturerequest.h:75
qgsmaptool.h
QgsRectangle::setXMaximum
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:135
qgsvectorlayer.h
QgsPointXY
Definition: qgspointxy.h:43
qgsmaptip.h
QgsVectorLayer::mapTipTemplate
QString mapTipTemplate
Definition: qgsvectorlayer.h:391
QgsMapTip::applyFontSettings
void applyFontSettings()
Apply font family and size to match user settings.
Definition: qgsmaptip.cpp:239
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:373
QgsVectorLayer
Definition: qgsvectorlayer.h:385
QgsMapLayer
Definition: qgsmaplayer.h:81
QgsPointXY::x
double x
Definition: qgspointxy.h:47
qgssettings.h
QgsMapTool::searchRadiusMU
static double searchRadiusMU(const QgsRenderContext &context)
Gets search radius in map units for given context.
Definition: qgsmaptool.cpp:215
QgsFeature
Definition: qgsfeature.h:55
qgswebview.h
qgslogger.h
QgsMapTip::QgsMapTip
QgsMapTip()
Default constructor.
Definition: qgsmaptip.cpp:42
QgsExpression
Definition: qgsexpression.h:113
QgsVectorLayer::displayExpression
QString displayExpression
Definition: qgsvectorlayer.h:390
QgsFeatureIterator
Definition: qgsfeatureiterator.h:263
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:184
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:51
QgsRectangle::setYMinimum
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:140
QgsRectangle::setYMaximum
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:145
QgsWebView
The QgsWebView class is a collection of stubs to mimic the API of QWebView on systems where the real ...
Definition: qgswebview.h:65