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