QGIS API Documentation  3.6.0-Noosa (5873452)
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  "white-space: nowrap;"
135  "font: %2pt \"%3\";" ).arg( backgroundColor ).arg( mFontSize ).arg( mFontFamily );
136 
137  containerStyle = QString(
138  "display: inline-block;"
139  "margin: 0px" );
140 
141  tipHtml = QString(
142  "<html>"
143  "<body style='%1'>"
144  "<div id='QgsWebViewContainer' style='%2'>%3</div>"
145  "</body>"
146  "</html>" ).arg( bodyStyle, containerStyle, tipText );
147 
148  QgsDebugMsg( tipHtml );
149 
150  mWidget->move( pixelPosition.x(),
151  pixelPosition.y() );
152 
153  mWebView->setHtml( tipHtml );
154  lastTipText = tipText;
155 
156  mWidget->show();
157 }
158 
159 void QgsMapTip::resizeContent()
160 {
161 #if WITH_QTWEBKIT
162  // Get the content size
163  QWebElement container = mWebView->page()->mainFrame()->findFirstElement(
164  QStringLiteral( "#QgsWebViewContainer" ) );
165  int width = container.geometry().width() + MARGIN_VALUE * 2;
166  int height = container.geometry().height() + MARGIN_VALUE * 2;
167  mWidget->resize( width, height );
168 #else
169  mWebView->adjustSize();
170 #endif
171 }
172 
174 {
175  if ( !mMapTipVisible )
176  return;
177 
178  mWebView->setHtml( QString() );
179  mWidget->hide();
180 
181  // Reset the visible flag
182  mMapTipVisible = false;
183 }
184 
185 QString QgsMapTip::fetchFeature( QgsMapLayer *layer, QgsPointXY &mapPosition, QgsMapCanvas *mapCanvas )
186 {
187  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
188  if ( !vlayer )
189  return QString();
190 
191  double searchRadius = QgsMapTool::searchRadiusMU( mapCanvas );
192 
193  QgsRectangle r;
194  r.setXMinimum( mapPosition.x() - searchRadius );
195  r.setYMinimum( mapPosition.y() - searchRadius );
196  r.setXMaximum( mapPosition.x() + searchRadius );
197  r.setYMaximum( mapPosition.y() + searchRadius );
198 
199  r = mapCanvas->mapSettings().mapToLayerCoordinates( layer, r );
200 
202  if ( mapCanvas )
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  QTime 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 }
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:64
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:214
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:173
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:239
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query 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...