QGIS API Documentation 3.99.0-Master (0c964c3d988)
Loading...
Searching...
No Matches
qgsfontutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsfontutils.h
3 ---------------------
4 begin : June 5, 2013
5 copyright : (C) 2013 by Larry Shaffer
6 email : larrys at dakotacarto 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
16#include "qgsfontutils.h"
17
18#include <memory>
19
20#include "qgis.h"
21#include "qgsapplication.h"
22#include "qgslogger.h"
23#include "qgssettings.h"
24
25#include <QApplication>
26#include <QFile>
27#include <QFont>
28#include <QFontDatabase>
29#include <QFontInfo>
30#include <QMimeData>
31#include <QString>
32#include <QStringList>
33
34using namespace Qt::StringLiterals;
35
36bool QgsFontUtils::fontMatchOnSystem( const QFont &f )
37{
38 const QFontInfo fi = QFontInfo( f );
39 return fi.exactMatch();
40}
41
42bool QgsFontUtils::fontFamilyOnSystem( const QString &family )
43{
44 const QFont tmpFont = QFont( family );
45 // compare just beginning of family string in case 'family [foundry]' differs
46 return tmpFont.family().startsWith( family, Qt::CaseInsensitive );
47}
48
49bool QgsFontUtils::fontFamilyHasStyle( const QString &family, const QString &style )
50{
51 const QFontDatabase fontDB;
52 if ( !fontFamilyOnSystem( family ) )
53 return false;
54
55 if ( fontDB.styles( family ).contains( style ) )
56 return true;
57
58 QString modified( style );
59#ifdef Q_OS_WIN
60 if ( style == "Roman" )
61 modified = "Normal";
62 else if ( style == "Oblique" )
63 modified = "Italic";
64 else if ( style == "Bold Oblique" )
65 modified = "Bold Italic";
66 if ( fontDB.styles( family ).contains( modified ) )
67 return true;
68#endif
69
70 // Deal with Qt6 Normal -> Regular style renaming
71 if ( modified == "Normal" )
72 modified = "Regular";
73 else if ( modified == QCoreApplication::translate( "QFontDatabase", "Normal" ) )
74 modified = QCoreApplication::translate( "QFontDatabase", "Regular" );
75 if ( fontDB.styles( family ).contains( modified ) )
76 return true;
77
78 return false;
79}
80
81QString QgsFontUtils::resolveFontStyleName( const QFont &font )
82{
83 auto styleNameIsMatch = [&font]( const QString & candidate ) -> bool
84 {
85 // confirm that style name matches bold/italic flags
86 QFont testFont( font.family() );
87 testFont.setStyleName( candidate );
88 return testFont.italic() == font.italic() && testFont.weight() == font.weight();
89 };
90
91 // attempt 1
92 const QFontInfo fontInfo( font );
93 QString styleName = fontInfo.styleName();
94 if ( !styleName.isEmpty() )
95 {
96 if ( styleNameIsMatch( styleName ) )
97 return styleName;
98 }
99
100 // attempt 2
101 styleName = QFontDatabase().styleString( font );
102 if ( !styleName.isEmpty() )
103 {
104 if ( styleNameIsMatch( styleName ) )
105 return styleName;
106 }
107
108 // failed
109 return QString();
110}
111
112bool QgsFontUtils::fontFamilyMatchOnSystem( const QString &family, QString *chosen, bool *match )
113{
114 const QFontDatabase fontDB;
115 const QStringList fontFamilies = fontDB.families();
116 bool found = false;
117
118 QList<QString>::const_iterator it = fontFamilies.constBegin();
119 for ( ; it != fontFamilies.constEnd(); ++it )
120 {
121 // first compare just beginning of 'family [foundry]' string
122 if ( it->startsWith( family, Qt::CaseInsensitive ) )
123 {
124 found = true;
125 // keep looking if match info is requested
126 if ( match )
127 {
128 // full 'family [foundry]' strings have to match
129 *match = ( *it == family );
130 if ( *match )
131 break;
132 }
133 else
134 {
135 break;
136 }
137 }
138 }
139
140 if ( found )
141 {
142 if ( chosen )
143 {
144 // retrieve the family actually assigned by matching algorithm
145 const QFont f = QFont( family );
146 *chosen = f.family();
147 }
148 }
149 else
150 {
151 if ( chosen )
152 {
153 *chosen = QString();
154 }
155
156 if ( match )
157 {
158 *match = false;
159 }
160 }
161
162 return found;
163}
164
165bool QgsFontUtils::updateFontViaStyle( QFont &f, const QString &fontstyle, bool fallback )
166{
167 if ( fontstyle.isEmpty() )
168 {
169 return false;
170 }
171
172 QFontDatabase fontDB;
173 QString actualFontStyle = fontstyle;
174
175 if ( !fallback )
176 {
177 // does the font even have the requested style?
178 bool hasstyle = fontFamilyHasStyle( f.family(), actualFontStyle );
179 if ( !hasstyle )
180 {
181 actualFontStyle = untranslateNamedStyle( fontstyle );
182 hasstyle = fontFamilyHasStyle( f.family(), actualFontStyle );
183 }
184
185 if ( !hasstyle )
186 {
187 return false;
188 }
189 }
190
191 // is the font's style already the same as requested?
192 if ( actualFontStyle == fontDB.styleString( f ) )
193 {
194 if ( f.styleName().isEmpty() )
195 {
196 f.setStyleName( actualFontStyle );
197 return true;
198 }
199 return false;
200 }
201
202 const QFont appfont = QApplication::font();
203 const int defaultSize = appfont.pointSize(); // QFontDatabase::font() needs an integer for size
204
205 QFont styledfont;
206 bool foundmatch = false;
207
208 // if fontDB.font() fails, it returns the default app font; but, that may be the target style
209 styledfont = fontDB.font( f.family(), actualFontStyle, defaultSize );
210 if ( appfont != styledfont || actualFontStyle != fontDB.styleString( f ) )
211 {
212 foundmatch = true;
213 }
214
215 // default to first found style if requested style is unavailable
216 // this helps in the situations where the passed-in font has to have a named style applied
217 if ( fallback && !foundmatch )
218 {
219 QFont testFont = QFont( f );
220 testFont.setPointSize( defaultSize );
221
222 // prefer a style that mostly matches the passed-in font
223 const auto constFamily = fontDB.styles( f.family() );
224 for ( const QString &style : constFamily )
225 {
226 styledfont = fontDB.font( f.family(), style, defaultSize );
227 styledfont = styledfont.resolve( f );
228 if ( testFont.toString() == styledfont.toString() )
229 {
230 foundmatch = true;
231 break;
232 }
233 }
234
235 // fallback to first style found that works
236 if ( !foundmatch )
237 {
238 for ( const QString &style : constFamily )
239 {
240 styledfont = fontDB.font( f.family(), style, defaultSize );
241 if ( QApplication::font() != styledfont )
242 {
243 foundmatch = true;
244 break;
245 }
246 }
247 }
248 }
249
250 // similar to QFont::resolve, but font may already have pixel size set
251 // and we want to make sure that's preserved
252 if ( foundmatch )
253 {
254 if ( !qgsDoubleNear( f.pointSizeF(), -1 ) )
255 {
256 styledfont.setPointSizeF( f.pointSizeF() );
257 }
258 else if ( f.pixelSize() != -1 )
259 {
260 styledfont.setPixelSize( f.pixelSize() );
261 }
262 styledfont.setCapitalization( f.capitalization() );
263 styledfont.setUnderline( f.underline() );
264 styledfont.setStrikeOut( f.strikeOut() );
265 styledfont.setWordSpacing( f.wordSpacing() );
266 styledfont.setLetterSpacing( QFont::AbsoluteSpacing, f.letterSpacing() );
267 f = styledfont;
268
269 return true;
270 }
271
272 return false;
273}
274
276{
277 return u"QGIS Vera Sans"_s;
278}
279
280bool QgsFontUtils::loadStandardTestFonts( const QStringList &loadstyles )
281{
282 // load standard test font from filesystem or testdata.qrc (for unit tests and general testing)
283 bool fontsLoaded = false;
284
285 QMap<QString, QString> fontStyles;
286 fontStyles.insert( u"Roman"_s, u"QGIS-Vera/QGIS-Vera.ttf"_s );
287 fontStyles.insert( u"Oblique"_s, u"QGIS-Vera/QGIS-VeraIt.ttf"_s );
288 fontStyles.insert( u"Bold"_s, u"QGIS-Vera/QGIS-VeraBd.ttf"_s );
289 fontStyles.insert( u"Bold Oblique"_s, u"QGIS-Vera/QGIS-VeraBI.ttf"_s );
290 fontStyles.insert( u"Deja Bold"_s, u"QGIS-DejaVu/QGISDejaVuSans-Bold.ttf"_s );
291
292 QMap<QString, QString>::const_iterator f = fontStyles.constBegin();
293 for ( ; f != fontStyles.constEnd(); ++f )
294 {
295 const QString fontpath( f.value() );
296 if ( !( loadstyles.contains( f.key() ) || loadstyles.contains( u"All"_s ) ) )
297 {
298 continue;
299 }
300
301 const QString fontFamily = !f.key().startsWith( "Deja"_L1 ) ? standardTestFontFamily() : u"QGIS DejaVu Sans"_s;
302 const QString fontstyle = !f.key().startsWith( "Deja"_L1 ) ? f.key() : f.key().mid( 5 );
303
304 if ( fontFamilyHasStyle( fontFamily, fontstyle ) )
305 {
306 QgsDebugMsgLevel( u"Test font '%1 %2' already available"_s.arg( fontFamily, fontstyle ), 2 );
307 }
308 else
309 {
310 bool loaded = false;
312 {
313 // workaround for bugs with Qt 4.8.5 (other versions?) on Mac 10.9, where fonts
314 // from qrc resources load but fail to work and default font is substituted [LS]:
315 // https://bugreports.qt.io/browse/QTBUG-30917
316 // https://bugreports.qt.io/browse/QTBUG-32789
317 const QString fontPath( QgsApplication::buildSourcePath() + "/tests/testdata/font/" + fontpath );
318 const int fontID = QFontDatabase::addApplicationFont( fontPath );
319 loaded = ( fontID != -1 );
320 fontsLoaded = ( fontsLoaded || loaded );
321 QgsDebugMsgLevel( u"Test font '%1 %2' %3 from filesystem [%4]"_s
322 .arg( fontFamily, fontstyle, loaded ? "loaded" : "FAILED to load", fontPath ), 2 );
323 QgsDebugMsgLevel( u"font families in %1: %2"_s.arg( fontID ).arg( QFontDatabase().applicationFontFamilies( fontID ).join( "," ) ), 2 );
324 }
325 else
326 {
327 QFile fontResource( ":/testdata/font/" + fontpath );
328 if ( fontResource.open( QIODevice::ReadOnly ) )
329 {
330 const int fontID = QFontDatabase::addApplicationFontFromData( fontResource.readAll() );
331 loaded = ( fontID != -1 );
332 fontsLoaded = ( fontsLoaded || loaded );
333 }
334 QgsDebugMsgLevel( u"Test font '%1' (%2) %3 from testdata.qrc"_s
335 .arg( fontFamily, fontstyle, loaded ? "loaded" : "FAILED to load" ), 2 );
336 }
337 }
338 }
339
340 return fontsLoaded;
341}
342
343QFont QgsFontUtils::getStandardTestFont( const QString &style, int pointsize )
344{
345 const QString fontFamily = !style.startsWith( "Deja"_L1 ) ? standardTestFontFamily() : u"QGIS DejaVu Sans"_s;
346 const QString fontStyle = !style.startsWith( "Deja"_L1 ) ? style : style.mid( 5 );
347
348 if ( ! fontFamilyHasStyle( fontFamily, fontStyle ) )
349 {
350 loadStandardTestFonts( QStringList() << style );
351 }
352
353 const QFontDatabase fontDB;
354 QFont f = fontDB.font( fontFamily, fontStyle, pointsize );
355#ifdef Q_OS_WIN
356 if ( !f.exactMatch() )
357 {
358 QString modified;
359 if ( fontStyle == "Roman" )
360 modified = "Normal";
361 else if ( fontStyle == "Oblique" )
362 modified = "Italic";
363 else if ( fontStyle == "Bold Oblique" )
364 modified = "Bold Italic";
365 if ( !modified.isEmpty() )
366 f = fontDB.font( fontFamily, modified, pointsize );
367 }
368 if ( !f.exactMatch() )
369 {
370 QgsDebugMsgLevel( u"Inexact font match - consider installing the %1 font."_s.arg( fontFamily ), 2 );
371 QgsDebugMsgLevel( u"Requested: %1"_s.arg( f.toString() ), 2 );
372 QFontInfo fi( f );
373 QgsDebugMsgLevel( u"Replaced: %1,%2,%3,%4,%5,%6,%7,%8,%9"_s.arg( fi.family() ).arg( fi.pointSizeF() ).arg( fi.pixelSize() ).arg( fi.styleHint() ).arg( fi.weight() ).arg( fi.style() ).arg( fi.underline() ).arg( fi.strikeOut() ).arg( fi.fixedPitch() ), 2 );
374 }
375#endif
376 // in case above statement fails to set style
377 f.setBold( fontStyle.contains( "Bold"_L1 ) );
378 f.setItalic( fontStyle.contains( "Oblique"_L1 ) || fontStyle.contains( "Italic"_L1 ) );
379
380 return f;
381}
382
383QDomElement QgsFontUtils::toXmlElement( const QFont &font, QDomDocument &document, const QString &elementName )
384{
385 QDomElement fontElem = document.createElement( elementName );
386 fontElem.setAttribute( u"description"_s, font.toString() );
387 fontElem.setAttribute( u"style"_s, untranslateNamedStyle( font.styleName() ) );
388 fontElem.setAttribute( u"bold"_s, font.bold() ? QChar( '1' ) : QChar( '0' ) );
389 fontElem.setAttribute( u"italic"_s, font.italic() ? QChar( '1' ) : QChar( '0' ) );
390 fontElem.setAttribute( u"underline"_s, font.underline() ? QChar( '1' ) : QChar( '0' ) );
391 fontElem.setAttribute( u"strikethrough"_s, font.strikeOut() ? QChar( '1' ) : QChar( '0' ) );
392 return fontElem;
393}
394
395bool QgsFontUtils::setFromXmlElement( QFont &font, const QDomElement &element )
396{
397 if ( element.isNull() )
398 {
399 return false;
400 }
401
402 font.fromString( element.attribute( u"description"_s ) );
403
404 if ( element.hasAttribute( u"bold"_s ) && element.attribute( u"bold"_s ) == QChar( '1' ) )
405 {
406 font.setBold( true );
407 }
408 if ( element.hasAttribute( u"italic"_s ) )
409 {
410 font.setItalic( element.attribute( u"italic"_s ) == QChar( '1' ) );
411 }
412 if ( element.hasAttribute( u"underline"_s ) )
413 {
414 font.setUnderline( element.attribute( u"underline"_s ) == QChar( '1' ) );
415 }
416 if ( element.hasAttribute( u"strikethrough"_s ) )
417 {
418 font.setStrikeOut( element.attribute( u"strikethrough"_s ) == QChar( '1' ) );
419 }
420
421 if ( element.hasAttribute( u"style"_s ) )
422 {
423 ( void )updateFontViaStyle( font, translateNamedStyle( element.attribute( u"style"_s ) ) );
424 }
425
426 return true;
427}
428
429bool QgsFontUtils::setFromXmlChildNode( QFont &font, const QDomElement &element, const QString &childNode )
430{
431 if ( element.isNull() )
432 {
433 return false;
434 }
435
436 const QDomNodeList nodeList = element.elementsByTagName( childNode );
437 if ( !nodeList.isEmpty() )
438 {
439 const QDomElement fontElem = nodeList.at( 0 ).toElement();
440 return setFromXmlElement( font, fontElem );
441 }
442 else
443 {
444 return false;
445 }
446}
447
448QMimeData *QgsFontUtils::toMimeData( const QFont &font )
449{
450 std::unique_ptr< QMimeData >mimeData( new QMimeData );
451
452 QDomDocument fontDoc;
453 const QDomElement fontElem = toXmlElement( font, fontDoc, u"font"_s );
454 fontDoc.appendChild( fontElem );
455 mimeData->setText( fontDoc.toString() );
456
457 return mimeData.release();
458}
459
460QFont QgsFontUtils::fromMimeData( const QMimeData *data, bool *ok )
461{
462 QFont font;
463 if ( ok )
464 *ok = false;
465
466 if ( !data )
467 return font;
468
469 const QString text = data->text();
470 if ( !text.isEmpty() )
471 {
472 QDomDocument doc;
473 QDomElement elem;
474
475 if ( doc.setContent( text ) )
476 {
477 elem = doc.documentElement();
478
479 if ( elem.nodeName() != "font"_L1 )
480 elem = elem.firstChildElement( u"font"_s );
481
482 if ( setFromXmlElement( font, elem ) )
483 {
484 if ( ok )
485 *ok = true;
486 }
487 return font;
488 }
489 }
490 return font;
491}
492
493static QMap<QString, QString> createTranslatedStyleMap()
494{
495 QMap<QString, QString> translatedStyleMap;
496 const QStringList words = QStringList()
497 << u"Normal"_s
498 << u"Regular"_s
499 << u"Light"_s
500 << u"Bold"_s
501 << u"Black"_s
502 << u"Demi"_s
503 << u"Italic"_s
504 << u"Oblique"_s;
505 const auto constWords = words;
506 for ( const QString &word : constWords )
507 {
508 translatedStyleMap.insert( QCoreApplication::translate( "QFontDatabase", qPrintable( word ) ), word );
509 }
510 return translatedStyleMap;
511}
512
513QString QgsFontUtils::translateNamedStyle( const QString &namedStyle )
514{
515 QStringList words = namedStyle.split( ' ', Qt::SkipEmptyParts );
516 for ( int i = 0, n = words.length(); i < n; ++i )
517 {
518 words[i] = QCoreApplication::translate( "QFontDatabase", words[i].toLocal8Bit().constData() );
519 }
520 return words.join( ' '_L1 );
521}
522
523QString QgsFontUtils::untranslateNamedStyle( const QString &namedStyle )
524{
525 static const QMap<QString, QString> translatedStyleMap = createTranslatedStyleMap();
526 QStringList words = namedStyle.split( ' ', Qt::SkipEmptyParts );
527
528 for ( int i = 0, n = words.length(); i < n; ++i )
529 {
530 if ( translatedStyleMap.contains( words[i] ) )
531 {
532 words[i] = translatedStyleMap.value( words[i] );
533 }
534 else
535 {
536 QgsDebugMsgLevel( u"Warning: style map does not contain %1"_s.arg( words[i] ), 2 );
537 }
538 }
539 return words.join( ' '_L1 );
540}
541
542QString QgsFontUtils::asCSS( const QFont &font, double pointToPixelScale )
543{
544 QString css = u"font-family: "_s + font.family() + ';';
545
546 //style
547 css += "font-style: "_L1;
548 switch ( font.style() )
549 {
550 case QFont::StyleNormal:
551 css += "normal"_L1;
552 break;
553 case QFont::StyleItalic:
554 css += "italic"_L1;
555 break;
556 case QFont::StyleOblique:
557 css += "oblique"_L1;
558 break;
559 }
560 css += ';';
561
562 //weight
563 int cssWeight = 400;
564 switch ( font.weight() )
565 {
566 case QFont::Light:
567 cssWeight = 300;
568 break;
569 case QFont::Normal:
570 cssWeight = 400;
571 break;
572 case QFont::DemiBold:
573 cssWeight = 600;
574 break;
575 case QFont::Bold:
576 cssWeight = 700;
577 break;
578 case QFont::Black:
579 cssWeight = 900;
580 break;
581 case QFont::Thin:
582 cssWeight = 100;
583 break;
584 case QFont::ExtraLight:
585 cssWeight = 200;
586 break;
587 case QFont::Medium:
588 cssWeight = 500;
589 break;
590 case QFont::ExtraBold:
591 cssWeight = 800;
592 break;
593 }
594 css += u"font-weight: %1;"_s.arg( cssWeight );
595
596 //size
597 css += u"font-size: %1px;"_s.arg( font.pointSizeF() >= 0 ? font.pointSizeF() * pointToPixelScale : font.pixelSize() );
598
599 return css;
600}
601
602void QgsFontUtils::addRecentFontFamily( const QString &family )
603{
604 if ( family.isEmpty() )
605 {
606 return;
607 }
608
609 QgsSettings settings;
610 QStringList recentFamilies = settings.value( u"fonts/recent"_s ).toStringList();
611
612 //remove matching families
613 recentFamilies.removeAll( family );
614
615 //then add to start of list
616 recentFamilies.prepend( family );
617
618 //trim to 10 fonts
619 recentFamilies = recentFamilies.mid( 0, 10 );
620
621 settings.setValue( u"fonts/recent"_s, recentFamilies );
622}
623
625{
626 const QgsSettings settings;
627 return settings.value( u"fonts/recent"_s ).toStringList();
628}
629
630void QgsFontUtils::setFontFamily( QFont &font, const QString &family )
631{
632 font.setFamily( family );
633 if ( !font.exactMatch() )
634 {
635 // some Qt versions struggle with fonts with certain unusual characters
636 // in their names, eg "ESRI Oil, Gas, & Water". Calling "setFamilies"
637 // can workaround these issues... (in some cases!)
638 font.setFamilies( { family } );
639 }
640}
641
642QFont QgsFontUtils::createFont( const QString &family, int pointSize, int weight, bool italic )
643{
644 QFont font( family, pointSize, weight, italic );
645 if ( !font.exactMatch() )
646 {
647 // some Qt versions struggle with fonts with certain unusual characters
648 // in their names, eg "ESRI Oil, Gas, & Water". Calling "setFamilies"
649 // can workaround these issues... (in some cases!)
650 font.setFamilies( { family } );
651 }
652 return font;
653}
static QString buildSourcePath()
Returns path to the source directory. Valid only when running from build directory.
static bool isRunningFromBuildDir()
Indicates whether running from build directory (not installed).
static QString resolveFontStyleName(const QFont &font)
Attempts to resolve the style name corresponding to the specified font object.
static QString asCSS(const QFont &font, double pointToPixelMultiplier=1.0)
Returns a CSS string representing the specified font as closely as possible.
static QString translateNamedStyle(const QString &namedStyle)
Returns the localized named style of a font, if such a translation is available.
static QString untranslateNamedStyle(const QString &namedStyle)
Returns the english named style of a font, if possible.
static bool setFromXmlElement(QFont &font, const QDomElement &element)
Sets the properties of a font to match the properties stored in an XML element.
static bool setFromXmlChildNode(QFont &font, const QDomElement &element, const QString &childNode)
Sets the properties of a font to match the properties stored in an XML child node.
static QFont createFont(const QString &family, int pointSize=-1, int weight=-1, bool italic=false)
Creates a font with the specified family.
static QMimeData * toMimeData(const QFont &font)
Returns new mime data representing the specified font settings.
static bool fontFamilyMatchOnSystem(const QString &family, QString *chosen=nullptr, bool *match=nullptr)
Check whether font family is on system.
static bool fontFamilyOnSystem(const QString &family)
Check whether font family is on system in a quick manner, which does not compare [foundry].
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
static bool fontMatchOnSystem(const QFont &f)
Check whether exact font is on system.
static bool loadStandardTestFonts(const QStringList &loadstyles)
Loads standard test fonts from filesystem or qrc resource.
static QFont getStandardTestFont(const QString &style="Roman", int pointsize=12)
Gets standard test font with specific style.
static void setFontFamily(QFont &font, const QString &family)
Sets the family for a font object.
static QDomElement toXmlElement(const QFont &font, QDomDocument &document, const QString &elementName)
Returns a DOM element containing the properties of the font.
static void addRecentFontFamily(const QString &family)
Adds a font family to the list of recently used font families.
static QString standardTestFontFamily()
Gets standard test font family.
static QFont fromMimeData(const QMimeData *data, bool *ok=nullptr)
Attempts to parse the provided mime data as a QFont.
static bool fontFamilyHasStyle(const QString &family, const QString &style)
Check whether font family on system has specific style.
static QStringList recentFontFamilies()
Returns a list of recently used font families.
Stores settings for use within QGIS.
Definition qgssettings.h:68
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6950
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63