QGIS API Documentation  2.99.0-Master (c558d51)
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 {
48 
49 }
50 
52  QgsPoint & mapPosition,
53  QPoint & thePixelPosition,
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  // Show the maptip on the canvas
63  QString tipText, lastTipText, tipHtml, bodyStyle, containerStyle,
64  backgroundColor, borderColor;
65 
66  delete mWidget;
67  mWidget = new QWidget( pMapCanvas );
68  mWebView = new QgsWebView( mWidget );
69 
70 
71 #if WITH_QTWEBKIT
72  mWebView->page()->setLinkDelegationPolicy( QWebPage::DelegateAllLinks );//Handle link clicks by yourself
73  mWebView->setContextMenuPolicy( Qt::NoContextMenu ); //No context menu is allowed if you don't need it
74  connect( mWebView, SIGNAL( linkClicked( QUrl ) ), this, SLOT( onLinkClicked( QUrl ) ) );
75 #endif
76 
77  mWebView->page()->settings()->setAttribute(
78  QWebSettings::DeveloperExtrasEnabled, true );
79  mWebView->page()->settings()->setAttribute(
80  QWebSettings::JavascriptEnabled, true );
81 
82  QHBoxLayout* layout = new QHBoxLayout;
83  layout->addWidget( mWebView );
84 
85  mWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
86  mWidget->setLayout( layout );
87 
88  //assure the map tip is never larger than half the map canvas
89  const int MAX_WIDTH = pMapCanvas->geometry().width() / 2;
90  const int MAX_HEIGHT = pMapCanvas->geometry().height() / 2;
91  mWidget->setMaximumSize( MAX_WIDTH, MAX_HEIGHT );
92 
93  // start with 0 size,
94  // the content will automatically make it grow up to MaximumSize
95  mWidget->resize( 0, 0 );
96 
97  backgroundColor = mWidget->palette().base().color().name();
98  borderColor = mWidget->palette().shadow().color().name();
99  mWidget->setStyleSheet( QString(
100  ".QWidget{"
101  "border: 1px solid %1;"
102  "background-color: %2;}" ).arg(
103  borderColor, backgroundColor ) );
104 
105  tipText = fetchFeature( pLayer, mapPosition, pMapCanvas );
106 
107  mMapTipVisible = !tipText.isEmpty();
108  if ( !mMapTipVisible )
109  {
110  clear();
111  return;
112  }
113 
114  if ( tipText == lastTipText )
115  {
116  return;
117  }
118 
119  bodyStyle = QString(
120  "background-color: %1;"
121  "margin: 0;" ).arg( backgroundColor );
122 
123  containerStyle = QString(
124  "display: inline-block;"
125  "margin: 0px" );
126 
127  tipHtml = QString(
128  "<html>"
129  "<body style='%1'>"
130  "<div id='QgsWebViewContainer' style='%2'>%3</div>"
131  "</body>"
132  "</html>" ).arg( bodyStyle, containerStyle, tipText );
133 
134  mWidget->move( thePixelPosition.x(),
135  thePixelPosition.y() );
136 
137  mWebView->setHtml( tipHtml );
138  lastTipText = tipText;
139 
140  mWidget->show();
141 
142 #if WITH_QTWEBKIT
143  int scrollbarWidth = mWebView->page()->mainFrame()->scrollBarGeometry(
144  Qt::Vertical ).width();
145  int scrollbarHeight = mWebView->page()->mainFrame()->scrollBarGeometry(
146  Qt::Horizontal ).height();
147 
148  if ( scrollbarWidth > 0 || scrollbarHeight > 0 )
149  {
150  // Get the content size
151  QWebElement container = mWebView->page()->mainFrame()->findFirstElement(
152  QStringLiteral( "#QgsWebViewContainer" ) );
153  int width = container.geometry().width() + 5 + scrollbarWidth;
154  int height = container.geometry().height() + 5 + scrollbarHeight;
155 
156  mWidget->resize( width, height );
157  }
158 #endif
159 }
160 
162 {
163  if ( !mMapTipVisible )
164  return;
165 
166  mWebView->setHtml( QString() );
167  mWidget->hide();
168 
169  // reset the visible flag
170  mMapTipVisible = false;
171 }
172 
173 QString QgsMapTip::fetchFeature( QgsMapLayer *layer, QgsPoint &mapPosition, QgsMapCanvas *mapCanvas )
174 {
175  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
176  if ( !vlayer )
177  return QString();
178 
179  double searchRadius = QgsMapTool::searchRadiusMU( mapCanvas );
180 
181  QgsRectangle r;
182  r.setXMinimum( mapPosition.x() - searchRadius );
183  r.setYMinimum( mapPosition.y() - searchRadius );
184  r.setXMaximum( mapPosition.x() + searchRadius );
185  r.setYMaximum( mapPosition.y() + searchRadius );
186 
187  r = mapCanvas->mapSettings().mapToLayerCoordinates( layer, r );
188 
189  QgsFeature feature;
190 
191  if ( !vlayer->getFeatures( QgsFeatureRequest().setFilterRect( r ).setFlags( QgsFeatureRequest::ExactIntersect ) ).nextFeature( feature ) )
192  return QString();
193 
194  QgsExpressionContext context;
198  if ( mapCanvas )
200 
201  context.setFeature( feature );
202 
203  QString mapTip = vlayer->mapTipTemplate();
204  if ( !mapTip.isEmpty() )
205  {
206  return QgsExpression::replaceExpressionText( mapTip, &context );
207  }
208  else
209  {
210  QgsExpression exp( vlayer->displayExpression() );
211  return exp.evaluate( &context ).toString();
212  }
213 }
214 
215 //This slot handles all clicks
216 void QgsMapTip::onLinkClicked( const QUrl &url )
217 {
218  QDesktopServices::openUrl( url );
219 }
Class for parsing and evaluation of expressions (formerly called "search strings").
A rectangle specified with double values.
Definition: qgsrectangle.h:35
Base class for all map layer types.
Definition: qgsmaplayer.h:49
double y
Definition: qgspoint.h:116
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:176
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.
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:135
QgsPoint mapToLayerCoordinates(QgsMapLayer *theLayer, QgsPoint point) const
transform point coordinates from output CRS to layer&#39;s CRS
virtual ~QgsMapTip()
Destructor.
Definition: qgsmaptip.cpp:46
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:106
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const
Query the layer for features specified in request.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:181
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:161
void showMapTip(QgsMapLayer *thepLayer, QgsPoint &theMapPosition, QPoint &thePixelPosition, QgsMapCanvas *mpMapCanvas)
Show a maptip at a given point on the map canvas.
Definition: qgsmaptip.cpp:51
This class wraps a request for features to a vector layer (or directly its vector data provider)...
The QgsWebView class is a collection of stubs to mimic the API of QWebView on systems where the real ...
Definition: qgswebview.h:57
A class to represent a point.
Definition: qgspoint.h:111
QString displayExpression
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:186
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QgsMapTip()
Default constructor.
Definition: qgsmaptip.cpp:39
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
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:171
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:115