QGIS API Documentation  2.99.0-Master (314842d)
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 "qgswebview.h"
23 #include "qgswebframe.h"
24 
25 // Qt includes
26 #include <QPoint>
27 #include <QToolTip>
28 #include <QSettings>
29 #include <QLabel>
30 #include <QDesktopServices>
31 #if WITH_QTWEBKIT
32 #include <QWebElement>
33 #endif
34 #include <QHBoxLayout>
35 
36 
37 #include "qgsmaptip.h"
38 
40  : mWidget( nullptr ), mWebView( nullptr )
41 {
42  // init the visible flag
43  mMapTipVisible = false;
44 }
45 
47  QgsPoint &mapPosition,
48  QPoint &pixelPosition,
49  QgsMapCanvas *pMapCanvas )
50 {
51  // Do the search using the active layer and the preferred label field for the
52  // layer. The label field must be defined in the layer configuration
53  // file/database. The code required to do this is similar to identify, except
54  // we only want the first qualifying feature and we will only display the
55  // field defined as the label field in the layer configuration file/database
56 
57  // Show the maptip on the canvas
58  QString tipText, lastTipText, tipHtml, bodyStyle, containerStyle,
59  backgroundColor, strokeColor;
60 
61  delete mWidget;
62  mWidget = new QWidget( pMapCanvas );
63  mWebView = new QgsWebView( mWidget );
64 
65 
66 #if WITH_QTWEBKIT
67  mWebView->page()->setLinkDelegationPolicy( QWebPage::DelegateAllLinks );//Handle link clicks by yourself
68  mWebView->setContextMenuPolicy( Qt::NoContextMenu ); //No context menu is allowed if you don't need it
69  connect( mWebView, SIGNAL( linkClicked( QUrl ) ), this, SLOT( onLinkClicked( QUrl ) ) );
70 #endif
71 
72  mWebView->page()->settings()->setAttribute(
73  QWebSettings::DeveloperExtrasEnabled, true );
74  mWebView->page()->settings()->setAttribute(
75  QWebSettings::JavascriptEnabled, true );
76 
77  QHBoxLayout *layout = new QHBoxLayout;
78  layout->addWidget( mWebView );
79 
80  mWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
81  mWidget->setLayout( layout );
82 
83  //assure the map tip is never larger than half the map canvas
84  const int MAX_WIDTH = pMapCanvas->geometry().width() / 2;
85  const int MAX_HEIGHT = pMapCanvas->geometry().height() / 2;
86  mWidget->setMaximumSize( MAX_WIDTH, MAX_HEIGHT );
87 
88  // start with 0 size,
89  // the content will automatically make it grow up to MaximumSize
90  mWidget->resize( 0, 0 );
91 
92  backgroundColor = mWidget->palette().base().color().name();
93  strokeColor = mWidget->palette().shadow().color().name();
94  mWidget->setStyleSheet( QString(
95  ".QWidget{"
96  "border: 1px solid %1;"
97  "background-color: %2;}" ).arg(
98  strokeColor, backgroundColor ) );
99 
100  tipText = fetchFeature( pLayer, mapPosition, pMapCanvas );
101 
102  mMapTipVisible = !tipText.isEmpty();
103  if ( !mMapTipVisible )
104  {
105  clear();
106  return;
107  }
108 
109  if ( tipText == lastTipText )
110  {
111  return;
112  }
113 
114  bodyStyle = QString(
115  "background-color: %1;"
116  "margin: 0;" ).arg( backgroundColor );
117 
118  containerStyle = QString(
119  "display: inline-block;"
120  "margin: 0px" );
121 
122  tipHtml = QString(
123  "<html>"
124  "<body style='%1'>"
125  "<div id='QgsWebViewContainer' style='%2'>%3</div>"
126  "</body>"
127  "</html>" ).arg( bodyStyle, containerStyle, tipText );
128 
129  mWidget->move( pixelPosition.x(),
130  pixelPosition.y() );
131 
132  mWebView->setHtml( tipHtml );
133  lastTipText = tipText;
134 
135  mWidget->show();
136 
137 #if WITH_QTWEBKIT
138  int scrollbarWidth = mWebView->page()->mainFrame()->scrollBarGeometry(
139  Qt::Vertical ).width();
140  int scrollbarHeight = mWebView->page()->mainFrame()->scrollBarGeometry(
141  Qt::Horizontal ).height();
142 
143  if ( scrollbarWidth > 0 || scrollbarHeight > 0 )
144  {
145  // Get the content size
146  QWebElement container = mWebView->page()->mainFrame()->findFirstElement(
147  QStringLiteral( "#QgsWebViewContainer" ) );
148  int width = container.geometry().width() + 5 + scrollbarWidth;
149  int height = container.geometry().height() + 5 + scrollbarHeight;
150 
151  mWidget->resize( width, height );
152  }
153 #endif
154 }
155 
157 {
158  if ( !mMapTipVisible )
159  return;
160 
161  mWebView->setHtml( QString() );
162  mWidget->hide();
163 
164  // reset the visible flag
165  mMapTipVisible = false;
166 }
167 
168 QString QgsMapTip::fetchFeature( QgsMapLayer *layer, QgsPoint &mapPosition, QgsMapCanvas *mapCanvas )
169 {
170  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
171  if ( !vlayer )
172  return QString();
173 
174  double searchRadius = QgsMapTool::searchRadiusMU( mapCanvas );
175 
176  QgsRectangle r;
177  r.setXMinimum( mapPosition.x() - searchRadius );
178  r.setYMinimum( mapPosition.y() - searchRadius );
179  r.setXMaximum( mapPosition.x() + searchRadius );
180  r.setYMaximum( mapPosition.y() + searchRadius );
181 
182  r = mapCanvas->mapSettings().mapToLayerCoordinates( layer, r );
183 
184  QgsFeature feature;
185 
186  if ( !vlayer->getFeatures( QgsFeatureRequest().setFilterRect( r ).setFlags( QgsFeatureRequest::ExactIntersect ) ).nextFeature( feature ) )
187  return QString();
188 
190  if ( mapCanvas )
192 
193  context.setFeature( feature );
194 
195  QString mapTip = vlayer->mapTipTemplate();
196  if ( !mapTip.isEmpty() )
197  {
198  return QgsExpression::replaceExpressionText( mapTip, &context );
199  }
200  else
201  {
202  QgsExpression exp( vlayer->displayExpression() );
203  return exp.evaluate( &context ).toString();
204  }
205 }
206 
207 //This slot handles all clicks
208 void QgsMapTip::onLinkClicked( const QUrl &url )
209 {
210  QDesktopServices::openUrl( url );
211 }
Class for parsing and evaluation of expressions (formerly called "search strings").
A rectangle specified with double values.
Definition: qgsrectangle.h:36
Base class for all map layer types.
Definition: qgsmaplayer.h:52
double y
Definition: qgspoint.h:42
static double searchRadiusMU(const QgsRenderContext &context)
Get search radius in map units for given context.
Definition: qgsmaptool.cpp:204
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:180
Use exact geometry intersection (slower) instead of bounding boxes.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QVariant evaluate()
Evaluate the feature and return the result.
QgsPoint mapToLayerCoordinates(const QgsMapLayer *layer, QgsPoint point) const
transform point coordinates from output CRS to layer&#39;s CRS
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:136
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:72
void showMapTip(QgsMapLayer *thepLayer, QgsPoint &mapPosition, QPoint &pixelPosition, QgsMapCanvas *mpMapCanvas)
Show a maptip at a given point on the map canvas.
Definition: qgsmaptip.cpp:46
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:185
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const
Query the layer for features specified in request.
void clear(QgsMapCanvas *mpMapCanvas=nullptr)
Clear the current maptip if it exists.
Definition: qgsmaptip.cpp:156
This class wraps a request for features to a vector layer (or directly its vector data provider)...
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:61
A class to represent a point.
Definition: qgspoint.h:37
QString displayExpression
const QgsMapSettings & mapSettings() const
Get 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:190
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QgsMapTip()
Default constructor.
Definition: qgsmaptip.cpp:39
bool nextFeature(QgsFeature &f)
Represents a vector layer which manages a vector based data sets.
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:175
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...
double x
Definition: qgspoint.h:41