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