QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 
26 // Qt includes
27 #include <QPoint>
28 #include <QToolTip>
29 #include <QSettings>
30 #include <QLabel>
31 #include <QDesktopServices>
32 #if WITH_QTWEBKIT
33 #include <QWebElement>
34 #endif
35 #include <QHBoxLayout>
36 
37 
38 #include "qgsmaptip.h"
39 
41 {
42  // Init the visible flag
43  mMapTipVisible = false;
44 
45  // Init font-related values
47 }
48 
50  QgsPointXY &mapPosition,
51  QPoint &pixelPosition,
52  QgsMapCanvas *pMapCanvas )
53 {
54  // Do the search using the active layer and the preferred label field for the
55  // layer. The label field must be defined in the layer configuration
56  // file/database. The code required to do this is similar to identify, except
57  // we only want the first qualifying feature and we will only display the
58  // field defined as the label field in the layer configuration file/database
59 
60  // Do not render map tips if the layer is not visible
61  if ( !pMapCanvas->layers().contains( pLayer ) )
62  {
63  return;
64  }
65 
66  // Show the maptip on the canvas
67  QString tipText, lastTipText, tipHtml, bodyStyle, containerStyle,
68  backgroundColor, strokeColor;
69 
70  delete mWidget;
71  mWidget = new QWidget( pMapCanvas );
72  mWidget->setContentsMargins( MARGIN_VALUE, MARGIN_VALUE, MARGIN_VALUE, MARGIN_VALUE );
73  mWebView = new QgsWebView( mWidget );
74 
75 
76 #if WITH_QTWEBKIT
77  mWebView->page()->setLinkDelegationPolicy( QWebPage::DelegateAllLinks );//Handle link clicks by yourself
78  mWebView->setContextMenuPolicy( Qt::NoContextMenu ); //No context menu is allowed if you don't need it
79  connect( mWebView, &QWebView::linkClicked, this, &QgsMapTip::onLinkClicked );
80  connect( mWebView, &QWebView::loadFinished, this, [ = ]( bool ) { resizeContent(); } );
81 #endif
82 
83  mWebView->page()->settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true );
84  mWebView->page()->settings()->setAttribute( QWebSettings::JavascriptEnabled, true );
85  mWebView->page()->settings()->setAttribute( QWebSettings::LocalStorageEnabled, true );
86 
87  // Disable scrollbars, avoid random resizing issues
88  mWebView->page()->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
89  mWebView->page()->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
90 
91  QHBoxLayout *layout = new QHBoxLayout;
92  layout->setContentsMargins( 0, 0, 0, 0 );
93  layout->addWidget( mWebView );
94 
95  mWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
96  mWidget->setLayout( layout );
97 
98  // Assure the map tip is never larger than half the map canvas
99  const int MAX_WIDTH = pMapCanvas->geometry().width() / 2;
100  const int MAX_HEIGHT = pMapCanvas->geometry().height() / 2;
101  mWidget->setMaximumSize( MAX_WIDTH, MAX_HEIGHT );
102 
103  // Start with 0 size,
104  // The content will automatically make it grow up to MaximumSize
105  mWidget->resize( 0, 0 );
106 
107  backgroundColor = mWidget->palette().base().color().name();
108  strokeColor = mWidget->palette().shadow().color().name();
109  mWidget->setStyleSheet( QStringLiteral(
110  ".QWidget{"
111  "border: 1px solid %1;"
112  "background-color: %2;}" ).arg(
113  strokeColor, backgroundColor ) );
114 
115  tipText = fetchFeature( pLayer, mapPosition, pMapCanvas );
116 
117  mMapTipVisible = !tipText.isEmpty();
118  if ( !mMapTipVisible )
119  {
120  clear();
121  return;
122  }
123 
124  if ( tipText == lastTipText )
125  {
126  return;
127  }
128 
129  bodyStyle = QStringLiteral(
130  "background-color: %1;"
131  "margin: 0;"
132  "font: %2pt \"%3\";" ).arg( backgroundColor ).arg( mFontSize ).arg( mFontFamily );
133 
134  containerStyle = QStringLiteral(
135  "display: inline-block;"
136  "margin: 0px" );
137 
138  tipHtml = QStringLiteral(
139  "<html>"
140  "<body style='%1'>"
141  "<div id='QgsWebViewContainer' style='%2'>%3</div>"
142  "</body>"
143  "</html>" ).arg( bodyStyle, containerStyle, tipText );
144 
145  QgsDebugMsg( tipHtml );
146 
147  mWidget->move( pixelPosition.x(),
148  pixelPosition.y() );
149 
150  mWebView->setHtml( tipHtml );
151  lastTipText = tipText;
152 
153  mWidget->show();
154 }
155 
156 void QgsMapTip::resizeContent()
157 {
158 #if WITH_QTWEBKIT
159  // Get the content size
160  QWebElement container = mWebView->page()->mainFrame()->findFirstElement(
161  QStringLiteral( "#QgsWebViewContainer" ) );
162  int width = container.geometry().width() + MARGIN_VALUE * 2;
163  int height = container.geometry().height() + MARGIN_VALUE * 2;
164  mWidget->resize( width, height );
165 #else
166  mWebView->adjustSize();
167 #endif
168 }
169 
171 {
172  if ( !mMapTipVisible )
173  return;
174 
175  mWebView->setHtml( QString() );
176  mWidget->hide();
177 
178  // Reset the visible flag
179  mMapTipVisible = false;
180 }
181 
182 QString QgsMapTip::fetchFeature( QgsMapLayer *layer, QgsPointXY &mapPosition, QgsMapCanvas *mapCanvas )
183 {
184  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
185  if ( !vlayer )
186  return QString();
187 
188  double searchRadius = QgsMapTool::searchRadiusMU( mapCanvas );
189 
190  QgsRectangle r;
191  r.setXMinimum( mapPosition.x() - searchRadius );
192  r.setYMinimum( mapPosition.y() - searchRadius );
193  r.setXMaximum( mapPosition.x() + searchRadius );
194  r.setYMaximum( mapPosition.y() + searchRadius );
195 
196  r = mapCanvas->mapSettings().mapToLayerCoordinates( layer, r );
197 
199  if ( mapCanvas )
201 
202  QString mapTip = vlayer->mapTipTemplate();
203  QString tipString;
204  QgsExpression exp( vlayer->displayExpression() );
205  QgsFeature feature;
207  if ( mapTip.isEmpty() )
208  {
209  exp.prepare( &context );
210  request.setSubsetOfAttributes( exp.referencedColumns(), vlayer->fields() );
211  }
212  QgsFeatureIterator it = vlayer->getFeatures( request );
213  QTime timer;
214  timer.start();
215  while ( it.nextFeature( feature ) )
216  {
217  context.setFeature( feature );
218  if ( !mapTip.isEmpty() )
219  {
220  tipString = QgsExpression::replaceExpressionText( mapTip, &context );
221  }
222  else
223  {
224  tipString = exp.evaluate( &context ).toString();
225  }
226 
227  if ( !tipString.isEmpty() || timer.elapsed() >= 1000 )
228  {
229  break;
230  }
231  }
232 
233  return tipString;
234 }
235 
237 {
238  QgsSettings settings;
239  QFont defaultFont = qApp->font();
240  mFontSize = settings.value( QStringLiteral( "/qgis/stylesheet/fontPointSize" ), defaultFont.pointSize() ).toInt();
241  mFontFamily = settings.value( QStringLiteral( "/qgis/stylesheet/fontFamily" ), defaultFont.family() ).toString();
242 }
243 
244 // This slot handles all clicks
245 void QgsMapTip::onLinkClicked( const QUrl &url )
246 {
247  QDesktopServices::openUrl( url );
248 }
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:40
Base class for all map layer types.
Definition: qgsmaplayer.h:63
void showMapTip(QgsMapLayer *thepLayer, QgsPointXY &mapPosition, QPoint &pixelPosition, QgsMapCanvas *mpMapCanvas)
Show a maptip at a given point on the map canvas.
Definition: qgsmaptip.cpp:49
static double searchRadiusMU(const QgsRenderContext &context)
Gets search radius in map units for given context.
Definition: qgsmaptool.cpp:215
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:134
Use exact geometry intersection (slower) instead of bounding boxes.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
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.
QList< QgsMapLayer * > layers() const
Returns the list of layers shown within the map canvas.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY 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:55
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:74
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:139
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:170
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
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:144
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:236
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query the layer for features specified in request.
QgsMapTip()
Default constructor.
Definition: qgsmaptip.cpp:40
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:129
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...