QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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"
19 #include "qgsencodingfiledialog.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 
30 namespace 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, QStringList( filterMap.keys() ).join( QLatin1String( ";;" ) ), &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, QStringList( filterMap.keys() ).join( ";;" ) ) );
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  {
311  {
312  return 0;
313  }
316  {
317  return std::numeric_limits<float>::digits10 + 1;
318  }
321  {
322  return std::numeric_limits<double>::digits10 + 1;
323  }
325  {
326  return std::numeric_limits<double>::digits10 + 1;
327  }
328  }
329  return 0;
330  }
331 }
332 
333 //
334 // QgsTemporaryCursorOverride
335 //
336 
338 {
339  QApplication::setOverrideCursor( cursor );
340 }
341 
343 {
344  if ( mHasOverride )
345  QApplication::restoreOverrideCursor();
346 }
347 
349 {
350  if ( !mHasOverride )
351  return;
352 
353  mHasOverride = false;
354  QApplication::restoreOverrideCursor();
355 }
356 
357 
358 //
359 // QgsTemporaryCursorRestoreOverride
360 //
361 
363 {
364  while ( QApplication::overrideCursor() )
365  {
366  mCursors.emplace_back( QCursor( *QApplication::overrideCursor() ) );
367  QApplication::restoreOverrideCursor();
368  }
369 }
370 
372 {
373  restore();
374 }
375 
377 {
378  for ( auto it = mCursors.rbegin(); it != mCursors.rend(); ++it )
379  {
380  QApplication::setOverrideCursor( *it );
381  }
382  mCursors.clear();
383 }
QgsTemporaryCursorRestoreOverride::~QgsTemporaryCursorRestoreOverride
~QgsTemporaryCursorRestoreOverride()
Definition: qgsguiutils.cpp:371
Qgis::DataType::CInt16
@ CInt16
Complex Int16.
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:161
Qgis::DataType::UInt32
@ UInt32
Thirty two bit unsigned integer (quint32)
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsGuiUtils::getFont
QFont getFont(bool &ok, const QFont &initial, const QString &title)
Show font selection dialog.
Definition: qgsguiutils.cpp:210
Qgis::DataType
DataType
Raster data types.
Definition: qgis.h:128
QgsEncodingFileDialog::cancelAll
bool cancelAll()
Returns true if the user clicked 'Cancel All'.
Definition: qgsencodingfiledialog.cpp:95
Qgis::DataType::Byte
@ Byte
Eight bit unsigned integer (quint8)
qgis.h
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
Qgis::DataType::Int32
@ Int32
Thirty two bit signed integer (qint32)
QgsGuiUtils::saveGeometry
void saveGeometry(QWidget *widget, const QString &keyName)
Save the wigget geometry into settings.
Definition: qgsguiutils.cpp:226
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsGuiUtils::restoreGeometry
bool restoreGeometry(QWidget *widget, const QString &keyName)
Restore the wigget geometry from settings.
Definition: qgsguiutils.cpp:233
QgsTemporaryCursorOverride::~QgsTemporaryCursorOverride
~QgsTemporaryCursorOverride()
Definition: qgsguiutils.cpp:342
QgsGuiUtils::iconSize
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
Definition: qgsguiutils.cpp:264
qgsapplication.h
QgsEncodingFileDialog
A file dialog which lets the user select the preferred encoding type for a data provider.
Definition: qgsencodingfiledialog.h:30
precision
int precision
Definition: qgswfsgetfeature.cpp:103
QgsGuiUtils::displayValueWithMaximumDecimals
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...
Definition: qgsguiutils.cpp:292
QgsGuiUtils
The QgsGuiUtils namespace contains constants and helper functions used throughout the QGIS GUI.
Definition: qgsguiutils.cpp:30
QgsEncodingFileDialog::addCancelAll
void addCancelAll()
Adds a 'Cancel All' button for the user to click.
Definition: qgsencodingfiledialog.cpp:85
Qgis::DataType::UnknownDataType
@ UnknownDataType
Unknown or unspecified type.
Qgis::DataType::Float64
@ Float64
Sixty four bit floating point (double)
Qgis::DataType::CInt32
@ CInt32
Complex Int32.
Qgis::DataType::Int16
@ Int16
Sixteen bit signed integer (qint16)
QgsTemporaryCursorRestoreOverride::restore
void restore()
Restores the cursor override early (i.e.
Definition: qgsguiutils.cpp:376
qgsencodingfiledialog.h
QgsSettings::setValue
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Definition: qgssettings.cpp:279
Qgis::DataType::ARGB32_Premultiplied
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
QgsGuiUtils::getSaveAsImageName
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:104
QgsGuiUtils::createWidgetKey
QString createWidgetKey(QWidget *widget, const QString &keyName)
Creates a key for the given widget that can be used to store related data in settings.
Definition: qgsguiutils.cpp:240
Qgis::DataType::UInt16
@ UInt16
Sixteen bit unsigned integer (quint16)
Qgis::DataType::ARGB32
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
QgsTemporaryCursorOverride::release
void release()
Releases the cursor override early (i.e.
Definition: qgsguiutils.cpp:348
Qgis::DataType::Float32
@ Float32
Thirty two bit floating point (float)
QgsApplication::scaleIconSize
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,...
Definition: qgsapplication.cpp:1980
qgssettings.h
Qgis::DataType::CFloat64
@ CFloat64
Complex Float64.
QgsTemporaryCursorRestoreOverride::QgsTemporaryCursorRestoreOverride
QgsTemporaryCursorRestoreOverride()
Constructor for QgsTemporaryCursorRestoreOverride.
Definition: qgsguiutils.cpp:362
QgsTemporaryCursorOverride::QgsTemporaryCursorOverride
QgsTemporaryCursorOverride(const QCursor &cursor)
Constructor for QgsTemporaryCursorOverride.
Definition: qgsguiutils.cpp:337
qgslogger.h
qgsguiutils.h
QgsGuiUtils::scaleIconSize
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
Definition: qgsguiutils.cpp:259
QgsGuiUtils::openFilesRememberingFilter
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:46
Qgis::DataType::CFloat32
@ CFloat32
Complex Float32.
QgsGuiUtils::significantDigits
int significantDigits(const Qgis::DataType rasterDataType)
Returns the maximum number of significant digits a for the given rasterDataType.
Definition: qgsguiutils.cpp:311
QgsGuiUtils::createFileFilter_
QString createFileFilter_(QString const &longName, QString const &glob)
Convenience function for readily creating file filters.
Definition: qgsguiutils.cpp:198
QgsGuiUtils::panelIconSize
QSize panelIconSize(QSize size)
Returns dockable panel toolbar icon width based on the provided window toolbar width.
Definition: qgsguiutils.cpp:278