QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
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 "qgsapplication.h"
18#include "qgssettings.h"
20#include "qgslogger.h"
21#include "qgis_gui.h"
22#include "qgis.h"
23
24#include <QImageWriter>
25#include <QFontDialog>
26#include <QApplication>
27#include <QRegularExpression>
28
29
30namespace QgsGuiUtils
31{
32
33 bool GUI_EXPORT openFilesRememberingFilter( QString const &filterName,
34 QString const &filters, QStringList &selectedFiles, QString &enc, QString &title,
35 bool cancelAll )
36 {
37 Q_UNUSED( enc )
38
39 QgsSettings settings;
40 QString lastUsedFilter = settings.value( "/UI/" + filterName, "" ).toString();
41 const QString lastUsedDir = settings.value( "/UI/" + filterName + "Dir", QDir::homePath() ).toString();
42
43 QgsDebugMsg( "Opening file dialog with filters: " + filters );
44 if ( !cancelAll )
45 {
46 selectedFiles = QFileDialog::getOpenFileNames( nullptr, title, lastUsedDir, filters, &lastUsedFilter );
47 }
48 else //we have to use non-native dialog to add cancel all button
49 {
50 QgsEncodingFileDialog *openFileDialog = new QgsEncodingFileDialog( nullptr, title, lastUsedDir, filters, QString() );
51
52 // allow for selection of more than one file
53 openFileDialog->setFileMode( QFileDialog::ExistingFiles );
54
55 if ( !lastUsedFilter.isEmpty() )
56 {
57 openFileDialog->selectNameFilter( lastUsedFilter );
58 }
59 openFileDialog->addCancelAll();
60 if ( openFileDialog->exec() == QDialog::Accepted )
61 {
62 selectedFiles = openFileDialog->selectedFiles();
63 }
64 else
65 {
66 //cancel or cancel all?
67 if ( openFileDialog->cancelAll() )
68 {
69 return true;
70 }
71 }
72 }
73
74 if ( !selectedFiles.isEmpty() )
75 {
76 // Fix by Tim - getting the dirPath from the dialog
77 // directly truncates the last node in the dir path.
78 // This is a workaround for that
79 const QString firstFileName = selectedFiles.first();
80 const QFileInfo fi( firstFileName );
81 const QString path = fi.path();
82
83 QgsDebugMsg( "Writing last used dir: " + path );
84
85 settings.setValue( "/UI/" + filterName, lastUsedFilter );
86 settings.setValue( "/UI/" + filterName + "Dir", path );
87 }
88 return false;
89 }
90
91 QPair<QString, QString> GUI_EXPORT getSaveAsImageName( QWidget *parent, const QString &message, const QString &defaultFilename )
92 {
93 // get a list of supported output image types
94 QMap<QString, QString> filterMap;
95 const auto supportedImageFormats { QImageWriter::supportedImageFormats() };
96 for ( const QByteArray &format : supportedImageFormats )
97 {
98 //svg doesn't work so skip it
99 if ( format == "svg" )
100 continue;
101
102 filterMap.insert( createFileFilter_( format ), format );
103 }
104
105#ifdef QGISDEBUG
106 QgsDebugMsgLevel( QStringLiteral( "Available Filters Map: " ), 2 );
107 for ( QMap<QString, QString>::iterator it = filterMap.begin(); it != filterMap.end(); ++it )
108 {
109 QgsDebugMsgLevel( it.key() + " : " + it.value(), 2 );
110 }
111#endif
112
113 QgsSettings settings; // where we keep last used filter in persistent state
114 const QString lastUsedDir = settings.value( QStringLiteral( "UI/lastSaveAsImageDir" ), QDir::homePath() ).toString();
115
116 // Prefer "png" format unless the user previously chose a different format
117 const QString pngExtension = QStringLiteral( "png" );
118 const QString pngFilter = createFileFilter_( pngExtension );
119 QString selectedFilter = settings.value( QStringLiteral( "UI/lastSaveAsImageFilter" ), pngFilter ).toString();
120
121 QString initialPath;
122 if ( defaultFilename.isNull() )
123 {
124 //no default filename provided, just use last directory
125 initialPath = lastUsedDir;
126 }
127 else
128 {
129 //a default filename was provided, so use it to build the initial path
130 initialPath = QDir( lastUsedDir ).filePath( defaultFilename );
131 }
132
133 QString outputFileName;
134 QString ext;
135#if defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(Q_OS_LINUX)
136 outputFileName = QFileDialog::getSaveFileName( parent, message, initialPath, qgsMapJoinKeys( filterMap, QStringLiteral( ";;" ) ), &selectedFilter );
137
138 if ( !outputFileName.isNull() )
139 {
140 ext = filterMap.value( selectedFilter, QString() );
141 if ( !ext.isNull() )
142 settings.setValue( QStringLiteral( "UI/lastSaveAsImageFilter" ), selectedFilter );
143 settings.setValue( QStringLiteral( "UI/lastSaveAsImageDir" ), QFileInfo( outputFileName ).absolutePath() );
144 }
145#else
146
147 //create a file dialog using the filter list generated above
148 std::unique_ptr<QFileDialog> fileDialog( new QFileDialog( parent, message, initialPath, qgsMapJoinKeys( filterMap, QStringLiteral( ";;" ) ) ) );
149
150 // allow for selection of more than one file
151 fileDialog->setFileMode( QFileDialog::AnyFile );
152 fileDialog->setAcceptMode( QFileDialog::AcceptSave );
153 fileDialog->setOption( QFileDialog::DontConfirmOverwrite, false );
154
155 if ( !selectedFilter.isEmpty() ) // set the filter to the last one used
156 {
157 fileDialog->selectNameFilter( selectedFilter );
158 }
159
160 //prompt the user for a fileName
161 if ( fileDialog->exec() == QDialog::Accepted )
162 {
163 outputFileName = fileDialog->selectedFiles().first();
164 }
165
166 selectedFilter = fileDialog->selectedNameFilter();
167 QgsDebugMsg( "Selected filter: " + selectedFilter );
168 ext = filterMap.value( selectedFilter, QString() );
169
170 if ( !ext.isNull() )
171 settings.setValue( "/UI/lastSaveAsImageFilter", selectedFilter );
172
173 settings.setValue( "/UI/lastSaveAsImageDir", fileDialog->directory().absolutePath() );
174#endif
175
176 // Add the file type suffix to the fileName if required
177 if ( !ext.isNull() && !outputFileName.endsWith( '.' + ext.toLower(), Qt::CaseInsensitive ) )
178 {
179 outputFileName += '.' + ext;
180 }
181
182 return qMakePair( outputFileName, ext );
183 }
184
185 QString createFileFilter_( QString const &longName, QString const &glob )
186 {
187 return QStringLiteral( "%1 (%2 %3)" ).arg( longName, glob.toLower(), glob.toUpper() );
188 }
189
190 QString createFileFilter_( QString const &format )
191 {
192 const QString longName = format.toUpper() + " format";
193 const QString glob = "*." + format;
194 return createFileFilter_( longName, glob );
195 }
196
197 QFont getFont( bool &ok, const QFont &initial, const QString &title )
198 {
199 // parent is intentionally not set to 'this' as
200 // that would make it follow the style sheet font
201 // see also #12233 and #4937
202#if defined(Q_OS_MAC)
203 // Native dialog broken on macOS with Qt5
204 // probably only broken in Qt5.11.1 and .2
205 // (see https://successfulsoftware.net/2018/11/02/qt-is-broken-on-macos-right-now/ )
206 // possible upstream bug: https://bugreports.qt.io/browse/QTBUG-69878 (fixed in Qt 5.12 ?)
207 return QFontDialog::getFont( &ok, initial, nullptr, title, QFontDialog::DontUseNativeDialog );
208#else
209 return QFontDialog::getFont( &ok, initial, nullptr, title );
210#endif
211 }
212
213 void saveGeometry( QWidget *widget, const QString &keyName )
214 {
215 QgsSettings settings;
216 const QString key = createWidgetKey( widget, keyName );
217 settings.setValue( key, widget->saveGeometry() );
218 }
219
220 bool restoreGeometry( QWidget *widget, const QString &keyName )
221 {
222 const QgsSettings settings;
223 const QString key = createWidgetKey( widget, keyName );
224 return widget->restoreGeometry( settings.value( key ).toByteArray() );
225 }
226
227 QString createWidgetKey( QWidget *widget, const QString &keyName )
228 {
229 QString subKey;
230 if ( !keyName.isEmpty() )
231 {
232 subKey = keyName;
233 }
234 else if ( widget->objectName().isEmpty() )
235 {
236 subKey = QString( widget->metaObject()->className() );
237 }
238 else
239 {
240 subKey = widget->objectName();
241 }
242 QString key = QStringLiteral( "Windows/%1/geometry" ).arg( subKey );
243 return key;
244 }
245
246 int scaleIconSize( int standardSize )
247 {
248 return QgsApplication::scaleIconSize( standardSize );
249 }
250
251 QSize iconSize( bool dockableToolbar )
252 {
253 const QgsSettings s;
254 const int w = s.value( QStringLiteral( "/qgis/iconSize" ), 32 ).toInt();
255 QSize size( w, w );
256
257 if ( dockableToolbar )
258 {
259 size = panelIconSize( size );
260 }
261
262 return size;
263 }
264
265 QSize panelIconSize( QSize size )
266 {
267 int adjustedSize = 16;
268 if ( size.width() > 32 )
269 {
270 adjustedSize = size.width() - 16;
271 }
272 else if ( size.width() == 32 )
273 {
274 adjustedSize = 24;
275 }
276 return QSize( adjustedSize, adjustedSize );
277 }
278
279 QString displayValueWithMaximumDecimals( const Qgis::DataType dataType, const double value, bool displayTrailingZeroes )
280 {
281 const int precision { significantDigits( dataType ) };
282 QString result { QLocale().toString( value, 'f', precision ) };
283 if ( ! displayTrailingZeroes )
284 {
285 const QRegularExpression zeroesRe { QStringLiteral( R"raw(\%1\d*?(0+$))raw" ).arg( QLocale().decimalPoint() ) };
286 if ( zeroesRe.match( result ).hasMatch() )
287 {
288 result.truncate( zeroesRe.match( result ).capturedStart( 1 ) );
289 if ( result.endsWith( QLocale().decimalPoint( ) ) )
290 {
291 result.chop( 1 );
292 }
293 }
294 }
295 return result;
296 }
297
298 int significantDigits( const Qgis::DataType rasterDataType )
299 {
300 switch ( rasterDataType )
301 {
312 {
313 return 0;
314 }
317 {
318 return std::numeric_limits<float>::digits10 + 1;
319 }
322 {
323 return std::numeric_limits<double>::digits10 + 1;
324 }
326 {
327 return std::numeric_limits<double>::digits10 + 1;
328 }
329 }
330 return 0;
331 }
332}
333
334//
335// QgsTemporaryCursorOverride
336//
337
339{
340 QApplication::setOverrideCursor( cursor );
341}
342
344{
345 if ( mHasOverride )
346 QApplication::restoreOverrideCursor();
347}
348
350{
351 if ( !mHasOverride )
352 return;
353
354 mHasOverride = false;
355 QApplication::restoreOverrideCursor();
356}
357
358
359//
360// QgsTemporaryCursorRestoreOverride
361//
362
364{
365 while ( QApplication::overrideCursor() )
366 {
367 mCursors.emplace_back( QCursor( *QApplication::overrideCursor() ) );
368 QApplication::restoreOverrideCursor();
369 }
370}
371
373{
374 restore();
375}
376
378{
379 for ( auto it = mCursors.rbegin(); it != mCursors.rend(); ++it )
380 {
381 QApplication::setOverrideCursor( *it );
382 }
383 mCursors.clear();
384}
DataType
Raster data types.
Definition: qgis.h:242
@ CInt32
Complex Int32.
@ Float32
Thirty two bit floating point (float)
@ CFloat64
Complex Float64.
@ Int16
Sixteen bit signed integer (qint16)
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30)
@ UInt16
Sixteen bit unsigned integer (quint16)
@ Byte
Eight bit unsigned integer (quint8)
@ UnknownDataType
Unknown or unspecified type.
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
@ Int32
Thirty two bit signed integer (qint32)
@ Float64
Sixty four bit floating point (double)
@ CFloat32
Complex Float32.
@ CInt16
Complex Int16.
@ UInt32
Thirty two bit unsigned integer (quint32)
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.
bool cancelAll()
Returns true if the user clicked 'Cancel All'.
void addCancelAll()
Adds a 'Cancel All' button for the user to click.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:63
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.
Definition: qgsguiutils.cpp:31
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.
Definition: qgsguiutils.cpp:91
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,...
Definition: qgsguiutils.cpp:33
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:3579
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
int precision