QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsguiutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsguiutils.cpp - Constants used throughout the QGIS GUI.
3 --------------------------------------
4 Date : 11-Jan-2006
5 Copyright : (C) 2006 by Tom Elwertowski
6 Email : telwertowski at users dot sourceforge dot net
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#include "qgsguiutils.h"
16
17#include "qgis.h"
18#include "qgis_gui.h"
19#include "qgsapplication.h"
21#include "qgsfileutils.h"
22#include "qgslogger.h"
23#include "qgssettings.h"
24
25#include <QApplication>
26#include <QFontDialog>
27#include <QImageWriter>
28#include <QRegularExpression>
29
30namespace QgsGuiUtils
31{
32
33 bool GUI_EXPORT openFilesRememberingFilter( QString const &filterName, QString const &filters, QStringList &selectedFiles, QString &enc, QString &title, bool cancelAll )
34 {
35 Q_UNUSED( enc )
36
37 QgsSettings settings;
38 QString lastUsedFilter = settings.value( "/UI/" + filterName, "" ).toString();
39 const QString lastUsedDir = settings.value( "/UI/" + filterName + "Dir", QDir::homePath() ).toString();
40
41 QgsDebugMsgLevel( "Opening file dialog with filters: " + filters, 3 );
42 if ( !cancelAll )
43 {
44 selectedFiles = QFileDialog::getOpenFileNames( nullptr, title, lastUsedDir, filters, &lastUsedFilter );
45 }
46 else //we have to use non-native dialog to add cancel all button
47 {
48 QgsEncodingFileDialog *openFileDialog = new QgsEncodingFileDialog( nullptr, title, lastUsedDir, filters, QString() );
49
50 // allow for selection of more than one file
51 openFileDialog->setFileMode( QFileDialog::ExistingFiles );
52
53 if ( !lastUsedFilter.isEmpty() )
54 {
55 openFileDialog->selectNameFilter( lastUsedFilter );
56 }
57 openFileDialog->addCancelAll();
58 if ( openFileDialog->exec() == QDialog::Accepted )
59 {
60 selectedFiles = openFileDialog->selectedFiles();
61 }
62 else
63 {
64 //cancel or cancel all?
65 if ( openFileDialog->cancelAll() )
66 {
67 return true;
68 }
69 }
70 }
71
72 if ( !selectedFiles.isEmpty() )
73 {
74 // Fix by Tim - getting the dirPath from the dialog
75 // directly truncates the last node in the dir path.
76 // This is a workaround for that
77 const QString firstFileName = selectedFiles.first();
78 const QFileInfo fi( firstFileName );
79 const QString path = fi.path();
80
81 QgsDebugMsgLevel( "Writing last used dir: " + path, 2 );
82
83 settings.setValue( "/UI/" + filterName, lastUsedFilter );
84 settings.setValue( "/UI/" + filterName + "Dir", path );
85 }
86 return false;
87 }
88
89 QPair<QString, QString> GUI_EXPORT getSaveAsImageName( QWidget *parent, const QString &message, const QString &defaultFilename )
90 {
91 // get a list of supported output image types
92 QMap<QString, QString> filterMap;
93 const auto supportedImageFormats { QImageWriter::supportedImageFormats() };
94 QStringList imageFormats;
95 // add PNG format first for certain file dialog to auto-fill from first listed extension
96 imageFormats << QStringLiteral( "*.png *.PNG" );
97 for ( const QByteArray &format : supportedImageFormats )
98 {
99 // svg doesn't work so skip it
100 if ( format == "svg" )
101 {
102 continue;
103 }
104
105 filterMap.insert( createFileFilter_( format ), format );
106
107 if ( format != "png" )
108 {
109 imageFormats << QStringLiteral( "*.%1 *.%2" ).arg( format, QString( format ).toUpper() );
110 }
111 }
112 const QString formatByExtension = QStringLiteral( "%1 (%2)" ).arg( QObject::tr( "Format by Extension" ), imageFormats.join( QLatin1Char( ' ' ) ) );
113
114#ifdef QGISDEBUG
115 QgsDebugMsgLevel( QStringLiteral( "Available Filters Map: " ), 2 );
116 for ( QMap<QString, QString>::iterator it = filterMap.begin(); it != filterMap.end(); ++it )
117 {
118 QgsDebugMsgLevel( it.key() + " : " + it.value(), 2 );
119 }
120#endif
121
122 QgsSettings settings; // where we keep last used filter in persistent state
123 const QString lastUsedDir = settings.value( QStringLiteral( "UI/lastSaveAsImageDir" ), QDir::homePath() ).toString();
124
125 QString selectedFilter = settings.value( QStringLiteral( "UI/lastSaveAsImageFilter" ), QString() ).toString();
126 if ( selectedFilter.isEmpty() )
127 {
128 selectedFilter = formatByExtension;
129 }
130
131 QString initialPath;
132 if ( defaultFilename.isNull() )
133 {
134 //no default filename provided, just use last directory
135 initialPath = lastUsedDir;
136 }
137 else
138 {
139 //a default filename was provided, so use it to build the initial path
140 initialPath = QDir( lastUsedDir ).filePath( defaultFilename );
141 }
142
143 QString outputFileName;
144 QString ext;
145#if defined( Q_OS_WIN ) || defined( Q_OS_MAC ) || defined( Q_OS_LINUX )
146 outputFileName = QFileDialog::getSaveFileName( parent, message, initialPath, formatByExtension + QStringLiteral( ";;" ) + qgsMapJoinKeys( filterMap, QStringLiteral( ";;" ) ), &selectedFilter );
147#else
148 //create a file dialog using the filter list generated above
149 auto fileDialog = std::make_unique<QFileDialog>( parent, message, initialPath, formatByExtension + QStringLiteral( ";;" ) + qgsMapJoinKeys( filterMap, QStringLiteral( ";;" ) ) );
150
151 // allow for selection of more than one file
152 fileDialog->setFileMode( QFileDialog::AnyFile );
153 fileDialog->setAcceptMode( QFileDialog::AcceptSave );
154 fileDialog->setOption( QFileDialog::DontConfirmOverwrite, false );
155
156 if ( !selectedFilter.isEmpty() ) // set the filter to the last one used
157 {
158 fileDialog->selectNameFilter( selectedFilter );
159 }
160
161 //prompt the user for a fileName
162 if ( fileDialog->exec() == QDialog::Accepted )
163 {
164 outputFileName = fileDialog->selectedFiles().first();
165 }
166#endif
167
168 if ( !outputFileName.isNull() )
169 {
170 if ( selectedFilter == formatByExtension )
171 {
172 settings.setValue( QStringLiteral( "UI/lastSaveAsImageFilter" ), QString() );
173 ext = QFileInfo( outputFileName ).suffix();
174
175 auto match = std::find_if( filterMap.begin(), filterMap.end(), [&ext]( const QString &filter ) { return filter == ext; } );
176 if ( match == filterMap.end() )
177 {
178 // Use "png" format when extension missing or not matching
179 ext = QStringLiteral( "png" );
180 selectedFilter = createFileFilter_( ext );
181 outputFileName = QgsFileUtils::addExtensionFromFilter( outputFileName, selectedFilter );
182 }
183 }
184 else
185 {
186 ext = filterMap.value( selectedFilter, QString() );
187 if ( !ext.isEmpty() )
188 {
189 outputFileName = QgsFileUtils::addExtensionFromFilter( outputFileName, selectedFilter );
190 settings.setValue( QStringLiteral( "UI/lastSaveAsImageFilter" ), selectedFilter );
191 }
192 }
193 settings.setValue( QStringLiteral( "UI/lastSaveAsImageDir" ), QFileInfo( outputFileName ).absolutePath() );
194 }
195
196 return qMakePair( outputFileName, ext );
197 }
198
199 QString createFileFilter_( QString const &longName, QString const &glob )
200 {
201 return QStringLiteral( "%1 (%2 %3)" ).arg( longName, glob.toLower(), glob.toUpper() );
202 }
203
204 QString createFileFilter_( QString const &format )
205 {
206 const QString longName = format.toUpper() + " format";
207 const QString glob = "*." + format;
208 return createFileFilter_( longName, glob );
209 }
210
211 QFont getFont( bool &ok, const QFont &initial, const QString &title )
212 {
213 // parent is intentionally not set to 'this' as
214 // that would make it follow the style sheet font
215 // see also #12233 and #4937
216#if defined( Q_OS_MAC )
217 // Native dialog broken on macOS with Qt5
218 // probably only broken in Qt5.11.1 and .2
219 // (see https://successfulsoftware.net/2018/11/02/qt-is-broken-on-macos-right-now/ )
220 // possible upstream bug: https://bugreports.qt.io/browse/QTBUG-69878 (fixed in Qt 5.12 ?)
221 return QFontDialog::getFont( &ok, initial, nullptr, title, QFontDialog::DontUseNativeDialog );
222#else
223 return QFontDialog::getFont( &ok, initial, nullptr, title );
224#endif
225 }
226
227 void saveGeometry( QWidget *widget, const QString &keyName )
228 {
229 QgsSettings settings;
230 const QString key = createWidgetKey( widget, keyName );
231 settings.setValue( key, widget->saveGeometry() );
232 }
233
234 bool restoreGeometry( QWidget *widget, const QString &keyName )
235 {
236 const QgsSettings settings;
237 const QString key = createWidgetKey( widget, keyName );
238 return widget->restoreGeometry( settings.value( key ).toByteArray() );
239 }
240
241 QString createWidgetKey( QWidget *widget, const QString &keyName )
242 {
243 QString subKey;
244 if ( !keyName.isEmpty() )
245 {
246 subKey = keyName;
247 }
248 else if ( widget->objectName().isEmpty() )
249 {
250 subKey = QString( widget->metaObject()->className() );
251 }
252 else
253 {
254 subKey = widget->objectName();
255 }
256 QString key = QStringLiteral( "Windows/%1/geometry" ).arg( subKey );
257 return key;
258 }
259
260 int scaleIconSize( int standardSize )
261 {
262 return QgsApplication::scaleIconSize( standardSize );
263 }
264
265 QSize iconSize( bool dockableToolbar )
266 {
267 const QgsSettings s;
268 const int w = s.value( QStringLiteral( "/qgis/toolbarIconSize" ), 32 ).toInt();
269 QSize size( w, w );
270
271 if ( dockableToolbar )
272 {
273 size = panelIconSize( size );
274 }
275
276 return size;
277 }
278
279 QSize panelIconSize( QSize size )
280 {
281 int adjustedSize = 16;
282 if ( size.width() > 32 )
283 {
284 adjustedSize = size.width() - 16;
285 }
286 else if ( size.width() == 32 )
287 {
288 adjustedSize = 24;
289 }
290 return QSize( adjustedSize, adjustedSize );
291 }
292
293 QString displayValueWithMaximumDecimals( const Qgis::DataType dataType, const double value, bool displayTrailingZeroes )
294 {
295 const int precision { significantDigits( dataType ) };
296 QString result { QLocale().toString( value, 'f', precision ) };
297 if ( !displayTrailingZeroes )
298 {
299 const QRegularExpression zeroesRe { QStringLiteral( R"raw(\%1\d*?(0+$))raw" ).arg( QLocale().decimalPoint() ) };
300 if ( zeroesRe.match( result ).hasMatch() )
301 {
302 result.truncate( zeroesRe.match( result ).capturedStart( 1 ) );
303 if ( result.endsWith( QLocale().decimalPoint() ) )
304 {
305 result.chop( 1 );
306 }
307 }
308 }
309 return result;
310 }
311
312 int significantDigits( const Qgis::DataType rasterDataType )
313 {
314 switch ( rasterDataType )
315 {
326 {
327 return 0;
328 }
331 {
332 return std::numeric_limits<float>::digits10 + 1;
333 }
336 {
337 return std::numeric_limits<double>::digits10 + 1;
338 }
340 {
341 return std::numeric_limits<double>::digits10 + 1;
342 }
343 }
344 return 0;
345 }
346} // namespace QgsGuiUtils
347
348//
349// QgsTemporaryCursorOverride
350//
351
353{
354 QApplication::setOverrideCursor( cursor );
355}
356
358{
359 if ( mHasOverride )
360 QApplication::restoreOverrideCursor();
361}
362
364{
365 if ( !mHasOverride )
366 return;
367
368 mHasOverride = false;
369 QApplication::restoreOverrideCursor();
370}
371
372
373//
374// QgsTemporaryCursorRestoreOverride
375//
376
378{
379 while ( QApplication::overrideCursor() )
380 {
381 mCursors.emplace_back( QCursor( *QApplication::overrideCursor() ) );
382 QApplication::restoreOverrideCursor();
383 }
384}
385
390
392{
393 for ( auto it = mCursors.rbegin(); it != mCursors.rend(); ++it )
394 {
395 QApplication::setOverrideCursor( *it );
396 }
397 mCursors.clear();
398}
399
400//
401// QWidgetUpdateBlocker
402//
403
405 : mWidget( widget )
406{
407 mWidget->setUpdatesEnabled( false );
408}
409
411{
412 if ( !mWidget )
413 return;
414
415 mWidget->setUpdatesEnabled( true );
416 mWidget = nullptr;
417}
418
QWidgetUpdateBlocker(QWidget *widget)
Constructor for QWidgetUpdateBlocker.
void release()
Releases the update block early (i.e.
DataType
Raster data types.
Definition qgis.h:372
@ CInt32
Complex Int32.
Definition qgis.h:383
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:380
@ CFloat64
Complex Float64.
Definition qgis.h:385
@ Int16
Sixteen bit signed integer (qint16).
Definition qgis.h:377
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition qgis.h:387
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30).
Definition qgis.h:375
@ UInt16
Sixteen bit unsigned integer (quint16).
Definition qgis.h:376
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:374
@ UnknownDataType
Unknown or unspecified type.
Definition qgis.h:373
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition qgis.h:386
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:379
@ Float64
Sixty four bit floating point (double).
Definition qgis.h:381
@ CFloat32
Complex Float32.
Definition qgis.h:384
@ CInt16
Complex Int16.
Definition qgis.h:382
@ UInt32
Thirty two bit unsigned integer (quint32).
Definition qgis.h:378
static int scaleIconSize(int standardSize, bool applyDevicePixelRatio=false)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
A file dialog which lets the user select the preferred encoding type for a data provider.
void addCancelAll()
Adds a 'Cancel All' button for the user to click.
bool cancelAll() const
Returns true if the user clicked 'Cancel All'.
static QString addExtensionFromFilter(const QString &fileName, const QString &filter)
Ensures that a fileName ends with an extension from the specified filter string.
Stores settings for use within QGIS.
Definition qgssettings.h:65
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.
QgsTemporaryCursorOverride(const QCursor &cursor)
Constructor for QgsTemporaryCursorOverride.
void release()
Releases the cursor override early (i.e.
QgsTemporaryCursorRestoreOverride()
Constructor for QgsTemporaryCursorRestoreOverride.
void restore()
Restores the cursor override early (i.e.
The QgsGuiUtils namespace contains constants and helper functions used throughout the QGIS GUI.
bool restoreGeometry(QWidget *widget, const QString &keyName)
Restore the wigget geometry from settings.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
QString createWidgetKey(QWidget *widget, const QString &keyName)
Creates a key for the given widget that can be used to store related data in settings.
QPair< QString, QString > GUI_EXPORT getSaveAsImageName(QWidget *parent, const QString &message, const QString &defaultFilename)
A helper function to get an image name from the user.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
int significantDigits(const Qgis::DataType rasterDataType)
Returns the maximum number of significant digits a for the given rasterDataType.
void saveGeometry(QWidget *widget, const QString &keyName)
Save the wigget geometry into settings.
QFont getFont(bool &ok, const QFont &initial, const QString &title)
Show font selection dialog.
bool GUI_EXPORT openFilesRememberingFilter(QString const &filterName, QString const &filters, QStringList &selectedFiles, QString &enc, QString &title, bool cancelAll)
Open files, preferring to have the default file selector be the last one used, if any; also,...
QString createFileFilter_(QString const &longName, QString const &glob)
Convenience function for readily creating file filters.
QSize panelIconSize(QSize size)
Returns dockable panel toolbar icon width based on the provided window toolbar width.
QString displayValueWithMaximumDecimals(const Qgis::DataType dataType, const double value, bool displayTrailingZeroes)
Returns a localized string representation of the value with the appropriate number of decimals suppor...
QString qgsMapJoinKeys(const QMap< Key, Value > &map, const QString &separator)
Joins all the map keys into a single string with each element separated by the given separator.
Definition qgis.h:6664
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61