QGIS API Documentation 3.41.0-Master (57ec4277f5e)
Loading...
Searching...
No Matches
qgslocatorwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslocatorwidget.cpp
3 --------------------
4 begin : May 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgslocator.h"
19#include "qgslocatormodel.h"
20#include "qgslocatorwidget.h"
21#include "moc_qgslocatorwidget.cpp"
23#include "qgsfilterlineedit.h"
24#include "qgsmapcanvas.h"
25#include "qgsapplication.h"
26#include "qgslogger.h"
27#include "qgsguiutils.h"
28
29#include <QLayout>
30#include <QCompleter>
31#include <QMenu>
32#include <QTextLayout>
33#include <QTextLine>
34
36const QgsSettingsEntryInteger *QgsLocatorWidget::settingLocatorTreeHeight = new QgsSettingsEntryInteger( QStringLiteral( "tree-height" ), sTreeGuiLocator, 20, QStringLiteral( "Number of rows to show in the locator tree (requires a restart)" ), Qgis::SettingsOptions(), 5 /*min*/, 100 /*max*/ );
38
40 : QWidget( parent )
41 , mModelBridge( new QgsLocatorModelBridge( this ) )
42 , mLineEdit( new QgsLocatorLineEdit( this ) )
43 , mResultsView( new QgsLocatorResultsView() )
44{
45 setObjectName( QStringLiteral( "LocatorWidget" ) );
46 mLineEdit->setShowClearButton( true );
47#ifdef Q_OS_MACOS
48 mLineEdit->setPlaceholderText( tr( "Type to locate (⌘K)" ) );
49#else
50 mLineEdit->setPlaceholderText( tr( "Type to locate (Ctrl+K)" ) );
51#endif
52
53 int placeholderMinWidth = mLineEdit->fontMetrics().boundingRect( mLineEdit->placeholderText() ).width();
54 int minWidth = std::max( 200, static_cast<int>( placeholderMinWidth * 1.8 ) );
55 resize( minWidth, 30 );
56 QSizePolicy sizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Preferred );
57 sizePolicy.setHorizontalStretch( 0 );
58 sizePolicy.setVerticalStretch( 0 );
59 setSizePolicy( sizePolicy );
60 setMinimumSize( QSize( minWidth, 0 ) );
61
62 QHBoxLayout *layout = new QHBoxLayout();
63 layout->setContentsMargins( 0, 0, 0, 0 );
64 layout->addWidget( mLineEdit );
65 setLayout( layout );
66
67 setFocusProxy( mLineEdit );
68
69 // setup floating container widget
70 mResultsContainer = new QgsFloatingWidget( parent ? parent->window() : nullptr );
71 mResultsContainer->setAnchorWidget( mLineEdit );
74
75 QHBoxLayout *containerLayout = new QHBoxLayout();
76 containerLayout->setContentsMargins( 0, 0, 0, 0 );
77 containerLayout->addWidget( mResultsView );
78 mResultsContainer->setLayout( containerLayout );
79 mResultsContainer->hide();
80
81 mResultsView->setModel( mModelBridge->proxyModel() );
82 mResultsView->setUniformRowHeights( true );
83
84 int iconSize = QgsGuiUtils::scaleIconSize( 16 );
85 mResultsView->setIconSize( QSize( iconSize, iconSize ) );
86 mResultsView->recalculateSize();
87 mResultsView->setContextMenuPolicy( Qt::CustomContextMenu );
88
89 connect( mLineEdit, &QLineEdit::textChanged, this, &QgsLocatorWidget::scheduleDelayedPopup );
90 connect( mResultsView, &QAbstractItemView::activated, this, &QgsLocatorWidget::acceptCurrentEntry );
91 connect( mResultsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsLocatorWidget::selectionChanged );
92 connect( mResultsView, &QAbstractItemView::customContextMenuRequested, this, &QgsLocatorWidget::showContextMenu );
93
94 connect( mModelBridge, &QgsLocatorModelBridge::resultAdded, this, &QgsLocatorWidget::resultAdded );
95 connect( mModelBridge, &QgsLocatorModelBridge::isRunningChanged, this, [=]() { mLineEdit->setShowSpinner( mModelBridge->isRunning() ); } );
96 connect( mModelBridge, &QgsLocatorModelBridge::resultsCleared, this, [=]() { mHasSelectedResult = false; } );
97
98 // have a tiny delay between typing text in line edit and showing the window
99 mPopupTimer.setInterval( 100 );
100 mPopupTimer.setSingleShot( true );
101 connect( &mPopupTimer, &QTimer::timeout, this, &QgsLocatorWidget::performSearch );
102 mFocusTimer.setInterval( 110 );
103 mFocusTimer.setSingleShot( true );
104 connect( &mFocusTimer, &QTimer::timeout, this, &QgsLocatorWidget::triggerSearchAndShowList );
105
106 mLineEdit->installEventFilter( this );
107 mResultsContainer->installEventFilter( this );
108 mResultsView->installEventFilter( this );
109 installEventFilter( this );
110 window()->installEventFilter( this );
111
112 mModelBridge->locator()->registerFilter( new QgsLocatorFilterFilter( this, this ) );
113
114 mMenu = new QMenu( this );
115 QAction *menuAction = mLineEdit->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/search.svg" ) ), QLineEdit::LeadingPosition );
116 connect( menuAction, &QAction::triggered, this, [=] {
117 mFocusTimer.stop();
118 mResultsContainer->hide();
119 mMenu->exec( QCursor::pos() );
120 } );
121 connect( mMenu, &QMenu::aboutToShow, this, &QgsLocatorWidget::configMenuAboutToShow );
122
123 mModelBridge->setTransformContext( QgsProject::instance()->transformContext() );
125 mModelBridge->setTransformContext( QgsProject::instance()->transformContext() );
126 } );
127}
128
130{
131 return mModelBridge->locator();
132}
133
135{
136 if ( mMapCanvas == canvas )
137 return;
138
139 for ( const QMetaObject::Connection &conn : std::as_const( mCanvasConnections ) )
140 {
141 disconnect( conn );
142 }
143 mCanvasConnections.clear();
144
145 mMapCanvas = canvas;
146 if ( mMapCanvas )
147 {
148 mModelBridge->updateCanvasExtent( mMapCanvas->mapSettings().visibleExtent() );
149 mModelBridge->updateCanvasCrs( mMapCanvas->mapSettings().destinationCrs() );
150 mCanvasConnections
151 << connect( mMapCanvas, &QgsMapCanvas::extentsChanged, this, [=]() { mModelBridge->updateCanvasExtent( mMapCanvas->mapSettings().visibleExtent() ); } )
152 << connect( mMapCanvas, &QgsMapCanvas::destinationCrsChanged, this, [=]() { mModelBridge->updateCanvasCrs( mMapCanvas->mapSettings().destinationCrs() ); } );
153 }
154}
155
156void QgsLocatorWidget::setPlaceholderText( const QString &text )
157{
158 mLineEdit->setPlaceholderText( text );
159}
160
162{
163 mResultsContainer->setAnchorPoint( anchorPoint );
164 mResultsContainer->setAnchorWidgetPoint( anchorWidgetPoint );
165}
166
167void QgsLocatorWidget::search( const QString &string )
168{
169 window()->activateWindow(); // window must also be active - otherwise floating docks can steal keystrokes
170 if ( string.isEmpty() )
171 {
172 mLineEdit->setFocus();
173 mLineEdit->selectAll();
174 }
175 else
176 {
177 scheduleDelayedPopup();
178 mLineEdit->setFocus();
179 mLineEdit->setText( string );
180 performSearch();
181 }
182}
183
185{
186 mModelBridge->invalidateResults();
187 mResultsContainer->hide();
188}
189
190void QgsLocatorWidget::scheduleDelayedPopup()
191{
192 mPopupTimer.start();
193}
194
195void QgsLocatorWidget::resultAdded()
196{
197 bool selectFirst = !mHasSelectedResult || mModelBridge->proxyModel()->rowCount() == 0;
198 if ( selectFirst )
199 {
200 int row = -1;
201 bool selectable = false;
202 while ( !selectable && row < mModelBridge->proxyModel()->rowCount() )
203 {
204 row++;
205 selectable = mModelBridge->proxyModel()->flags( mModelBridge->proxyModel()->index( row, 0 ) ).testFlag( Qt::ItemIsSelectable );
206 }
207 if ( selectable )
208 mResultsView->setCurrentIndex( mModelBridge->proxyModel()->index( row, 0 ) );
209 }
210}
211
212void QgsLocatorWidget::showContextMenu( const QPoint &point )
213{
214 QModelIndex index = mResultsView->indexAt( point );
215 if ( !index.isValid() )
216 return;
217
218 const QList<QgsLocatorResult::ResultAction> actions = mResultsView->model()->data( index, static_cast<int>( QgsLocatorModel::CustomRole::ResultActions ) ).value<QList<QgsLocatorResult::ResultAction>>();
219 QMenu *contextMenu = new QMenu( mResultsView );
220 for ( auto resultAction : actions )
221 {
222 QAction *menuAction = new QAction( resultAction.text, contextMenu );
223 if ( !resultAction.iconPath.isEmpty() )
224 menuAction->setIcon( QIcon( resultAction.iconPath ) );
225 connect( menuAction, &QAction::triggered, this, [=]() { mModelBridge->triggerResult( index, resultAction.id ); } );
226 contextMenu->addAction( menuAction );
227 }
228 contextMenu->exec( mResultsView->viewport()->mapToGlobal( point ) );
229}
230
231void QgsLocatorWidget::performSearch()
232{
233 mPopupTimer.stop();
234 mModelBridge->performSearch( mLineEdit->text() );
235 showList();
236}
237
238void QgsLocatorWidget::showList()
239{
240 mResultsContainer->show();
241 mResultsContainer->raise();
242}
243
244void QgsLocatorWidget::triggerSearchAndShowList()
245{
246 if ( mModelBridge->proxyModel()->rowCount() == 0 )
247 performSearch();
248 else
249 showList();
250}
251
252bool QgsLocatorWidget::eventFilter( QObject *obj, QEvent *event )
253{
254 if ( obj == mLineEdit && event->type() == QEvent::KeyPress )
255 {
256 QKeyEvent *keyEvent = static_cast<QKeyEvent *>( event );
257 switch ( keyEvent->key() )
258 {
259 case Qt::Key_Up:
260 case Qt::Key_Down:
261 case Qt::Key_PageUp:
262 case Qt::Key_PageDown:
263 triggerSearchAndShowList();
264 mHasSelectedResult = true;
265 QgsApplication::sendEvent( mResultsView, event );
266 return true;
267 case Qt::Key_Home:
268 case Qt::Key_End:
269 if ( keyEvent->modifiers() & Qt::ControlModifier )
270 {
271 triggerSearchAndShowList();
272 mHasSelectedResult = true;
273 QgsApplication::sendEvent( mResultsView, event );
274 return true;
275 }
276 break;
277 case Qt::Key_Enter:
278 case Qt::Key_Return:
279 acceptCurrentEntry();
280 return true;
281 case Qt::Key_Escape:
282 mResultsContainer->hide();
283 return true;
284 case Qt::Key_Tab:
285 if ( !mLineEdit->performCompletion() )
286 {
287 mHasSelectedResult = true;
288 mResultsView->selectNextResult();
289 }
290 return true;
291 case Qt::Key_Backtab:
292 mHasSelectedResult = true;
293 mResultsView->selectPreviousResult();
294 return true;
295 default:
296 break;
297 }
298 }
299 else if ( obj == mResultsView && event->type() == QEvent::MouseButtonPress )
300 {
301 mHasSelectedResult = true;
302 }
303 else if ( event->type() == QEvent::FocusOut && ( obj == mLineEdit || obj == mResultsContainer || obj == mResultsView ) )
304 {
305 if ( !mLineEdit->hasFocus() && !mResultsContainer->hasFocus() && !mResultsView->hasFocus() )
306 {
307 mFocusTimer.stop();
308 mResultsContainer->hide();
309 }
310 }
311 else if ( event->type() == QEvent::FocusIn && obj == mLineEdit )
312 {
313 mFocusTimer.start();
314 }
315 else if ( obj == window() && event->type() == QEvent::Resize )
316 {
317 mResultsView->recalculateSize();
318 }
319 return QWidget::eventFilter( obj, event );
320}
321
322void QgsLocatorWidget::configMenuAboutToShow()
323{
324 mMenu->clear();
325 for ( QgsLocatorFilter *filter : mModelBridge->locator()->filters() )
326 {
327 if ( !filter->enabled() )
328 continue;
329
330 QAction *action = new QAction( filter->displayName(), mMenu );
331 connect( action, &QAction::triggered, this, [=] {
332 QString currentText = mLineEdit->text();
333 if ( currentText.isEmpty() )
334 currentText = tr( "<type here>" );
335 else
336 {
337 QStringList parts = currentText.split( ' ' );
338 if ( parts.count() > 1 && mModelBridge->locator()->filters( parts.at( 0 ) ).count() > 0 )
339 {
340 parts.pop_front();
341 currentText = parts.join( ' ' );
342 }
343 }
344
345 mLineEdit->setText( filter->activePrefix() + ' ' + currentText );
346 mLineEdit->setSelection( filter->activePrefix().length() + 1, currentText.length() );
347 } );
348 mMenu->addAction( action );
349 }
350 mMenu->addSeparator();
351 QAction *configAction = new QAction( tr( "Configure…" ), mMenu );
352 connect( configAction, &QAction::triggered, this, &QgsLocatorWidget::configTriggered );
353 mMenu->addAction( configAction );
354}
355
356
357void QgsLocatorWidget::acceptCurrentEntry()
358{
359 if ( mModelBridge->hasQueueRequested() )
360 {
361 return;
362 }
363 else
364 {
365 if ( !mResultsView->isVisible() )
366 return;
367
368 QModelIndex index = mResultsView->currentIndex();
369 if ( !index.isValid() )
370 return;
371
372 mResultsContainer->hide();
373 mLineEdit->clearFocus();
374 mModelBridge->triggerResult( index );
375 }
376}
377
378void QgsLocatorWidget::selectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
379{
380 if ( !mResultsView->isVisible() )
381 return;
382
383 mModelBridge->selectionChanged( selected, deselected );
384}
385
387
388//
389// QgsLocatorResultsView
390//
391
392QgsLocatorResultsView::QgsLocatorResultsView( QWidget *parent )
393 : QTreeView( parent )
394{
395 setRootIsDecorated( false );
396 setUniformRowHeights( true );
397 header()->hide();
398 header()->setStretchLastSection( true );
399}
400
401void QgsLocatorResultsView::recalculateSize()
402{
403 QStyleOptionViewItem optView;
404#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
405 optView.init( this );
406#else
407 optView.initFrom( this );
408#endif
409
410 // try to show about 20 rows
411 int rowSize = QgsLocatorWidget::settingLocatorTreeHeight->value() * itemDelegate()->sizeHint( optView, model()->index( 0, 0 ) ).height();
412
413 // try to take up a sensible portion of window width (about half)
414 int width = std::max( 300, window()->size().width() / 2 );
415 QSize newSize( width, rowSize + frameWidth() * 2 );
416 // resize the floating widget this is contained within
417 parentWidget()->resize( newSize );
418 QTreeView::resize( newSize );
419
420 header()->resizeSection( 0, width / 2 );
421 header()->resizeSection( 1, 0 );
422}
423
424void QgsLocatorResultsView::selectNextResult()
425{
426 const int rowCount = model()->rowCount( QModelIndex() );
427 if ( rowCount == 0 )
428 return;
429
430 int nextRow = currentIndex().row() + 1;
431 nextRow = nextRow % rowCount;
432 setCurrentIndex( model()->index( nextRow, 0 ) );
433}
434
435void QgsLocatorResultsView::selectPreviousResult()
436{
437 const int rowCount = model()->rowCount( QModelIndex() );
438 if ( rowCount == 0 )
439 return;
440
441 int previousRow = currentIndex().row() - 1;
442 if ( previousRow < 0 )
443 previousRow = rowCount - 1;
444 setCurrentIndex( model()->index( previousRow, 0 ) );
445}
446
447//
448// QgsLocatorFilterFilter
449//
450
451QgsLocatorFilterFilter::QgsLocatorFilterFilter( QgsLocatorWidget *locator, QObject *parent )
452 : QgsLocatorFilter( parent )
453 , mLocator( locator )
454{}
455
456QgsLocatorFilterFilter *QgsLocatorFilterFilter::clone() const
457{
458 return new QgsLocatorFilterFilter( mLocator );
459}
460
461QgsLocatorFilter::Flags QgsLocatorFilterFilter::flags() const
462{
464}
465
466void QgsLocatorFilterFilter::fetchResults( const QString &string, const QgsLocatorContext &, QgsFeedback *feedback )
467{
468 if ( !string.isEmpty() )
469 {
470 //only shows results when nothing typed
471 return;
472 }
473
474 for ( QgsLocatorFilter *filter : mLocator->locator()->filters() )
475 {
476 if ( feedback->isCanceled() )
477 return;
478
479 if ( filter == this || !filter || !filter->enabled() )
480 continue;
481
482 QgsLocatorResult result;
483 result.displayString = filter->activePrefix();
484 result.description = filter->displayName();
485 result.setUserData( QString( filter->activePrefix() + ' ' ) );
486 result.icon = QgsApplication::getThemeIcon( QStringLiteral( "/search.svg" ) );
487 emit resultFetched( result );
488 }
489}
490
491void QgsLocatorFilterFilter::triggerResult( const QgsLocatorResult &result )
492{
493 mLocator->search( result.userData().toString() );
494}
495
496QgsLocatorLineEdit::QgsLocatorLineEdit( QgsLocatorWidget *locator, QWidget *parent )
497 : QgsFilterLineEdit( parent )
498 , mLocatorWidget( locator )
499{
500 connect( mLocatorWidget->locator(), &QgsLocator::searchPrepared, this, [&] { update(); } );
501}
502
503void QgsLocatorLineEdit::paintEvent( QPaintEvent *event )
504{
505 // this adds the completion as grey text at the right of the cursor
506 // see https://stackoverflow.com/a/50425331/1548052
507 // this is possible that the completion might be badly rendered if the cursor is larger than the line edit
508 // this sounds acceptable as it is not very likely to use completion for super long texts
509 // for more details see https://stackoverflow.com/a/54218192/1548052
510
511 QLineEdit::paintEvent( event );
512
513 if ( !hasFocus() )
514 return;
515
516 QString currentText = text();
517
518 if ( currentText.length() == 0 || cursorPosition() < currentText.length() )
519 return;
520
521 const QStringList completionList = mLocatorWidget->locator()->completionList();
522
523 mCompletionText.clear();
524 QString completion;
525 for ( const QString &candidate : completionList )
526 {
527 if ( candidate.startsWith( currentText ) )
528 {
529 completion = candidate.right( candidate.length() - currentText.length() );
530 mCompletionText = candidate;
531 break;
532 }
533 }
534
535 if ( completion.isEmpty() )
536 return;
537
538 ensurePolished(); // ensure font() is up to date
539
540 QRect cr = cursorRect();
541 QPoint pos = cr.topRight() - QPoint( cr.width() / 2, 0 );
542
543 QTextLayout l( completion, font() );
544 l.beginLayout();
545 QTextLine line = l.createLine();
546 line.setLineWidth( width() - pos.x() );
547 line.setPosition( pos );
548 l.endLayout();
549
550 QPainter p( this );
551 p.setPen( QPen( Qt::gray, 1 ) );
552 l.draw( &p, QPoint( 0, 0 ) );
553}
554
555bool QgsLocatorLineEdit::performCompletion()
556{
557 if ( !mCompletionText.isEmpty() )
558 {
559 setText( mCompletionText );
560 mCompletionText.clear();
561 return true;
562 }
563 else
564 return false;
565}
566
567
QFlags< SettingsOption > SettingsOptions
Definition qgis.h:693
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
QLineEdit subclass with built in support for clearing the widget's value and handling custom null val...
A QWidget subclass for creating widgets which float outside of the normal Qt layout system.
void setAnchorWidget(QWidget *widget)
Sets the widget to "anchor" the floating widget to.
void setAnchorWidgetPoint(AnchorPoint point)
Returns the anchor widget's anchor point, which corresponds to the point on the anchor widget which t...
AnchorPoint
Reference points for anchoring widget position.
@ BottomLeft
Bottom-left of widget.
@ TopLeft
Top-left of widget.
void setAnchorPoint(AnchorPoint point)
Sets the floating widget's anchor point, which corresponds to the point on the widget which should re...
Encapsulates the properties relating to the context of a locator search.
Abstract base class for filters which collect locator results.
QFlags< Flag > Flags
@ FlagFast
Filter finds results quickly and can be safely run in the main thread.
The QgsLocatorModelBridge class provides the core functionality to be used in a locator widget.
Q_INVOKABLE QgsLocatorProxyModel * proxyModel() const
Returns the proxy model.
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
This will call filters implementation of selection/deselection of results.
void isRunningChanged()
Emitted when the running status changes.
void resultAdded()
Emitted when a result is added.
void triggerResult(const QModelIndex &index, const int actionId=-1)
Triggers the result at given index and with optional actionId if an additional action was triggered.
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the coordinate transform context, which should be used whenever the locator constructs a coordin...
QgsLocator * locator() const
Returns the locator.
bool hasQueueRequested() const
Returns true if some text to be search is pending in the queue.
Q_INVOKABLE void performSearch(const QString &text)
Perform a search.
void resultsCleared()
Emitted when the results are cleared.
void updateCanvasCrs(const QgsCoordinateReferenceSystem &crs)
Update the canvas CRS used to create search context.
void updateCanvasExtent(const QgsRectangle &extent)
Update the canvas extent used to create search context.
void invalidateResults()
This will invalidate current search results.
@ ResultActions
The actions to be shown for the given result in a context menu.
Encapsulates properties of an individual matching result found by a QgsLocatorFilter.
QString description
Descriptive text for result.
void setUserData(const QVariant &userData)
Set userData for the locator result.
QString displayString
String displayed for result.
QIcon icon
Icon for result.
A special locator widget which allows searching for matching results from a QgsLocator and presenting...
static const QgsSettingsEntryInteger * settingLocatorTreeHeight
void setResultContainerAnchors(QgsFloatingWidget::AnchorPoint anchorPoint, QgsFloatingWidget::AnchorPoint anchorWidgetPoint)
Sets the result container anchorPoint and anchorWidgetPoint position.
void configTriggered()
Emitted when the configure option is triggered in the widget.
void setPlaceholderText(const QString &text)
Set placeholder text for the line edit.
void setMapCanvas(QgsMapCanvas *canvas)
Sets a map canvas to associate with the widget.
void search(const QString &string)
Triggers the locator widget to focus, open and start searching for a specified string.
QgsLocatorWidget(QWidget *parent SIP_TRANSFERTHIS=nullptr)
Constructor for QgsLocatorWidget.
bool eventFilter(QObject *obj, QEvent *event) override
void invalidateResults()
Invalidates the current search results, e.g.
QgsLocator * locator()
Returns a pointer to the locator utilized by this widget.
Handles the management of QgsLocatorFilter objects and async collection of search results from them.
Definition qgslocator.h:61
void searchPrepared()
Emitted when locator has prepared the search (.
void registerFilter(QgsLocatorFilter *filter)
Registers a filter within the locator.
QList< QgsLocatorFilter * > filters(const QString &prefix=QString())
Returns the list of filters registered in the locator.
Map canvas is a class for displaying all GIS data types on a canvas.
void extentsChanged()
Emitted when the extents of the map change.
void destinationCrsChanged()
Emitted when map CRS has changed.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes output image size into account.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
static QgsProject * instance()
Returns the QgsProject singleton instance.
void transformContextChanged()
Emitted when the project transformContext() is changed.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
An integer settings entry.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...