QGIS API Documentation  3.19.0-Master (26212d215f)
qgsrasterformatsaveoptionswidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrasterformatsaveoptionswidget.cpp
3  -------------------
4  begin : July 2012
5  copyright : (C) 2012 by Etienne Tourigny
6  email : etourigny dot dev at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
19 #include "qgslogger.h"
20 #include "qgsdialog.h"
21 #include "qgsrasterlayer.h"
22 #include "qgsproviderregistry.h"
23 #include "qgsrasterdataprovider.h"
24 #include "qgssettings.h"
25 #include "qgsgdalutils.h"
26 
27 #include <QInputDialog>
28 #include <QMessageBox>
29 #include <QTextEdit>
30 #include <QMouseEvent>
31 #include <QMenu>
32 
33 
34 QMap< QString, QStringList > QgsRasterFormatSaveOptionsWidget::sBuiltinProfiles;
35 
36 static const QString PYRAMID_JPEG_YCBCR_COMPRESSION( QStringLiteral( "JPEG_QUALITY_OVERVIEW=75 COMPRESS_OVERVIEW=JPEG PHOTOMETRIC_OVERVIEW=YCBCR INTERLEAVE_OVERVIEW=PIXEL" ) );
37 static const QString PYRAMID_JPEG_COMPRESSION( QStringLiteral( "JPEG_QUALITY_OVERVIEW=75 COMPRESS_OVERVIEW=JPEG INTERLEAVE_OVERVIEW=PIXEL" ) );
38 
40  QgsRasterFormatSaveOptionsWidget::Type type, const QString &provider )
41  : QWidget( parent )
42  , mFormat( format )
43  , mProvider( provider )
44 {
45  setupUi( this );
46  connect( mProfileNewButton, &QPushButton::clicked, this, &QgsRasterFormatSaveOptionsWidget::mProfileNewButton_clicked );
47  connect( mProfileDeleteButton, &QPushButton::clicked, this, &QgsRasterFormatSaveOptionsWidget::mProfileDeleteButton_clicked );
48  connect( mProfileResetButton, &QPushButton::clicked, this, &QgsRasterFormatSaveOptionsWidget::mProfileResetButton_clicked );
49  connect( mOptionsAddButton, &QPushButton::clicked, this, &QgsRasterFormatSaveOptionsWidget::mOptionsAddButton_clicked );
50  connect( mOptionsDeleteButton, &QPushButton::clicked, this, &QgsRasterFormatSaveOptionsWidget::mOptionsDeleteButton_clicked );
51  connect( mOptionsLineEdit, &QLineEdit::editingFinished, this, &QgsRasterFormatSaveOptionsWidget::mOptionsLineEdit_editingFinished );
52 
53  setType( type );
54 
55  if ( sBuiltinProfiles.isEmpty() )
56  {
57  // key=profileKey values=format,profileName,options
58  sBuiltinProfiles[ QStringLiteral( "z_adefault" )] = ( QStringList() << QString() << tr( "Default" ) << QString() );
59 
60  // these GTiff profiles are based on Tim's benchmarks at
61  // http://linfiniti.com/2011/05/gdal-efficiency-of-various-compression-algorithms/
62  // big: no compression | medium: reasonable size/speed tradeoff | small: smallest size
63  sBuiltinProfiles[ QStringLiteral( "z_gtiff_1big" )] =
64  ( QStringList() << QStringLiteral( "GTiff" ) << tr( "No Compression" )
65  << QStringLiteral( "COMPRESS=NONE BIGTIFF=IF_NEEDED" ) );
66  sBuiltinProfiles[ QStringLiteral( "z_gtiff_2medium" )] =
67  ( QStringList() << QStringLiteral( "GTiff" ) << tr( "Low Compression" )
68  << QStringLiteral( "COMPRESS=PACKBITS" ) );
69  sBuiltinProfiles[ QStringLiteral( "z_gtiff_3small" )] =
70  ( QStringList() << QStringLiteral( "GTiff" ) << tr( "High Compression" )
71  << QStringLiteral( "COMPRESS=DEFLATE PREDICTOR=2 ZLEVEL=9" ) );
72  sBuiltinProfiles[ QStringLiteral( "z_gtiff_4jpeg" )] =
73  ( QStringList() << QStringLiteral( "GTiff" ) << tr( "JPEG Compression" )
74  << QStringLiteral( "COMPRESS=JPEG JPEG_QUALITY=75" ) );
75 
76  // overview compression schemes for GTiff format, see
77  // http://www.gdal.org/gdaladdo.html and http://www.gdal.org/frmt_gtiff.html
78  // TODO - should we offer GDAL_TIFF_OVR_BLOCKSIZE option here or in QgsRasterPyramidsOptionsWidget ?
79  sBuiltinProfiles[ QStringLiteral( "z__pyramids_gtiff_1big" )] =
80  ( QStringList() << QStringLiteral( "_pyramids" ) << tr( "No Compression" )
81  << QStringLiteral( "COMPRESS_OVERVIEW=NONE BIGTIFF_OVERVIEW=IF_NEEDED" ) );
82  sBuiltinProfiles[ QStringLiteral( "z__pyramids_gtiff_2medium" )] =
83  ( QStringList() << QStringLiteral( "_pyramids" ) << tr( "Low Compression" )
84  << QStringLiteral( "COMPRESS_OVERVIEW=PACKBITS" ) );
85  sBuiltinProfiles[ QStringLiteral( "z__pyramids_gtiff_3small" )] =
86  ( QStringList() << QStringLiteral( "_pyramids" ) << tr( "High Compression" )
87  << QStringLiteral( "COMPRESS_OVERVIEW=DEFLATE PREDICTOR_OVERVIEW=2 ZLEVEL=9" ) ); // how to set zlevel?
88  sBuiltinProfiles[ QStringLiteral( "z__pyramids_gtiff_4jpeg" )] =
89  ( QStringList() << QStringLiteral( "_pyramids" ) << tr( "JPEG Compression" )
90  << PYRAMID_JPEG_YCBCR_COMPRESSION );
91  }
92 
93  connect( mProfileComboBox, &QComboBox::currentTextChanged,
94  this, &QgsRasterFormatSaveOptionsWidget::updateOptions );
95  connect( mOptionsTable, &QTableWidget::cellChanged, this, &QgsRasterFormatSaveOptionsWidget::optionsTableChanged );
96  connect( mOptionsHelpButton, &QAbstractButton::clicked, this, &QgsRasterFormatSaveOptionsWidget::helpOptions );
97  connect( mOptionsValidateButton, &QAbstractButton::clicked, this, [ = ] { validateOptions(); } );
98 
99  // create eventFilter to map right click to swapOptionsUI()
100  // mOptionsLabel->installEventFilter( this );
101  mOptionsLineEdit->installEventFilter( this );
102  mOptionsStackedWidget->installEventFilter( this );
103 
104  updateControls();
105  updateProfiles();
106 
107  QgsDebugMsg( QStringLiteral( "done" ) );
108 }
109 
110 void QgsRasterFormatSaveOptionsWidget::setFormat( const QString &format )
111 {
112  mFormat = format;
113  updateControls();
114  updateProfiles();
115 }
116 
117 void QgsRasterFormatSaveOptionsWidget::setProvider( const QString &provider )
118 {
119  mProvider = provider;
120  updateControls();
121 }
122 
123 // show/hide widgets - we need this function if widget is used in creator
125 {
126  QList< QWidget * > widgets = this->findChildren<QWidget *>();
127  if ( ( type == Table ) || ( type == LineEdit ) )
128  {
129  // hide all controls, except stacked widget
130  const auto constWidgets = widgets;
131  for ( QWidget *widget : constWidgets )
132  widget->setVisible( false );
133  mOptionsStackedWidget->setVisible( true );
134  const auto children { mOptionsStackedWidget->findChildren<QWidget *>() };
135  for ( QWidget *widget : children )
136  widget->setVisible( true );
137 
138  // show relevant page
139  if ( type == Table )
140  swapOptionsUI( 0 );
141  else if ( type == LineEdit )
142  swapOptionsUI( 1 );
143  }
144  else
145  {
146  // show all widgets, except profile buttons (unless Full)
147  const auto constWidgets = widgets;
148  for ( QWidget *widget : constWidgets )
149  widget->setVisible( true );
150  if ( type != Full )
151  mProfileButtons->setVisible( false );
152 
153  // show elevant page
154  if ( type == ProfileLineEdit )
155  swapOptionsUI( 1 );
156  }
157 }
158 
159 QString QgsRasterFormatSaveOptionsWidget::pseudoFormat() const
160 {
161  return mPyramids ? QStringLiteral( "_pyramids" ) : mFormat;
162 }
163 
165 {
166  // build profiles list = user + builtin(last)
167  QString format = pseudoFormat();
168  QStringList profileKeys = profiles();
169  QMapIterator<QString, QStringList> it( sBuiltinProfiles );
170  while ( it.hasNext() )
171  {
172  it.next();
173  QString profileKey = it.key();
174  if ( ! profileKeys.contains( profileKey ) && !it.value().isEmpty() )
175  {
176  // insert key if is for all formats or this format (GTiff)
177  if ( it.value()[0].isEmpty() || it.value()[0] == format )
178  {
179  profileKeys.insert( 0, profileKey );
180  }
181  }
182  }
183  std::sort( profileKeys.begin(), profileKeys.end() );
184 
185  // populate mOptionsMap and mProfileComboBox
186  mOptionsMap.clear();
187  mProfileComboBox->blockSignals( true );
188  mProfileComboBox->clear();
189  const auto constProfileKeys = profileKeys;
190  for ( const QString &profileKey : constProfileKeys )
191  {
192  QString profileName, profileOptions;
193  profileOptions = createOptions( profileKey );
194  if ( sBuiltinProfiles.contains( profileKey ) )
195  {
196  profileName = sBuiltinProfiles[ profileKey ][ 1 ];
197  if ( profileOptions.isEmpty() )
198  profileOptions = sBuiltinProfiles[ profileKey ][ 2 ];
199  }
200  else
201  {
202  profileName = profileKey;
203  }
204  mOptionsMap[ profileKey ] = profileOptions;
205  mProfileComboBox->addItem( profileName, profileKey );
206  }
207 
208  // update UI
209  mProfileComboBox->blockSignals( false );
210  // mProfileComboBox->setCurrentIndex( 0 );
211  QgsSettings mySettings;
212  mProfileComboBox->setCurrentIndex( mProfileComboBox->findData( mySettings.value(
213  mProvider + "/driverOptions/" + format.toLower() + "/defaultProfile",
214  "z_adefault" ) ) );
215  updateOptions();
216 }
217 
218 void QgsRasterFormatSaveOptionsWidget::updateOptions()
219 {
220  mBlockOptionUpdates++;
221  QString myOptions = mOptionsMap.value( currentProfileKey() );
222 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
223  QStringList myOptionsList = myOptions.trimmed().split( ' ', QString::SkipEmptyParts );
224 #else
225  QStringList myOptionsList = myOptions.trimmed().split( ' ', Qt::SkipEmptyParts );
226 #endif
227 
228  // If the default JPEG compression profile was selected, remove PHOTOMETRIC_OVERVIEW=YCBCR
229  // if the raster is not RGB. Otherwise this is bound to fail afterwards.
230  if ( mRasterLayer && mRasterLayer->bandCount() != 3 &&
231  myOptions == PYRAMID_JPEG_YCBCR_COMPRESSION )
232  {
233  myOptions = PYRAMID_JPEG_COMPRESSION;
234  }
235 
236  if ( mOptionsStackedWidget->currentIndex() == 0 )
237  {
238  mOptionsTable->setRowCount( 0 );
239  for ( int i = 0; i < myOptionsList.count(); i++ )
240  {
241  QStringList key_value = myOptionsList[i].split( '=' );
242  if ( key_value.count() == 2 )
243  {
244  mOptionsTable->insertRow( i );
245  mOptionsTable->setItem( i, 0, new QTableWidgetItem( key_value[0] ) );
246  mOptionsTable->setItem( i, 1, new QTableWidgetItem( key_value[1] ) );
247  }
248  }
249  }
250  else
251  {
252  mOptionsLineEdit->setText( myOptions );
253  mOptionsLineEdit->setCursorPosition( 0 );
254  }
255 
256  mBlockOptionUpdates--;
257  emit optionsChanged();
258 }
259 
261 {
262  setCreateOptions();
263 }
264 
266 {
267  QString message;
268 
269  if ( mProvider == QLatin1String( "gdal" ) && !mFormat.isEmpty() && ! mPyramids )
270  {
271  message = QgsGdalUtils::helpCreationOptionsFormat( mFormat );
272  if ( message.isEmpty() )
273  message = tr( "Cannot get create options for driver %1" ).arg( mFormat );
274  }
275  else if ( mProvider == QLatin1String( "gdal" ) && mPyramids )
276  {
277  message = tr( "For details on pyramids options please see the following pages" );
278  message += QLatin1String( "\n\nhttps://gdal.org/programs/gdaladdo.html\n\nhttps://gdal.org/drivers/raster/gtiff.html" );
279  }
280  else
281  message = tr( "No help available" );
282 
283  // show simple non-modal dialog - should we make the basic xml prettier?
284  QgsDialog *dlg = new QgsDialog( this );
285  dlg->setWindowTitle( tr( "Create Options for %1" ).arg( mFormat ) );
286  QTextEdit *textEdit = new QTextEdit( dlg );
287  textEdit->setReadOnly( true );
288  // message = tr( "Create Options:\n\n%1" ).arg( message );
289  textEdit->setText( message );
290  dlg->layout()->addWidget( textEdit );
291  dlg->resize( 600, 400 );
292 #ifdef Q_OS_MAC
293  dlg->exec(); //modal
294 #else
295  dlg->show(); //non modal
296 #endif
297 }
298 
299 QString QgsRasterFormatSaveOptionsWidget::validateOptions( bool gui, bool reportOK )
300 {
301  QStringList createOptions = options();
302  QString message;
303 
304  QgsDebugMsg( QStringLiteral( "layer: [%1] file: [%2] format: [%3]" ).arg( mRasterLayer ? mRasterLayer->id() : "none", mRasterFileName, mFormat ) );
305  // if no rasterLayer is defined, but we have a raster fileName, then create a temp. rasterLayer to validate options
306  // ideally we should keep it for future access, but this is trickier
307  QgsRasterLayer *rasterLayer = mRasterLayer;
308  bool tmpLayer = false;
309  if ( !( mRasterLayer && rasterLayer->dataProvider() ) && ! mRasterFileName.isNull() )
310  {
311  tmpLayer = true;
313  options.skipCrsValidation = true;
314  rasterLayer = new QgsRasterLayer( mRasterFileName, QFileInfo( mRasterFileName ).baseName(), QStringLiteral( "gdal" ), options );
315  }
316 
317  if ( mProvider == QLatin1String( "gdal" ) && mPyramids )
318  {
319  if ( rasterLayer && rasterLayer->dataProvider() )
320  {
321  QgsDebugMsg( QStringLiteral( "calling validate pyramids on layer's data provider" ) );
322  message = rasterLayer->dataProvider()->validatePyramidsConfigOptions( mPyramidsFormat, createOptions, mFormat );
323  }
324  else
325  {
326  message = tr( "cannot validate pyramid options" );
327  }
328  }
329  else if ( !createOptions.isEmpty() && mProvider == QLatin1String( "gdal" ) && !mFormat.isEmpty() )
330  {
331  if ( rasterLayer && rasterLayer->dataProvider() )
332  {
333  QgsDebugMsg( QStringLiteral( "calling validate on layer's data provider" ) );
334  message = rasterLayer->dataProvider()->validateCreationOptions( createOptions, mFormat );
335  }
336  else
337  {
338  // get validateCreationOptionsFormat() function ptr for provider
339  message = QgsGdalUtils::validateCreationOptionsFormat( createOptions, mFormat );
340 
341  }
342  }
343  else if ( ! createOptions.isEmpty() )
344  {
345  QMessageBox::information( this, QString(), tr( "Cannot validate creation options." ), QMessageBox::Close );
346  if ( tmpLayer )
347  delete rasterLayer;
348  return QString();
349  }
350 
351  if ( gui )
352  {
353  if ( message.isNull() )
354  {
355  if ( reportOK )
356  QMessageBox::information( this, QString(), tr( "Valid" ), QMessageBox::Close );
357  }
358  else
359  {
360  QMessageBox::warning( this, QString(), tr( "Invalid %1:\n\n%2\n\nClick on help button to get valid creation options for this format." ).arg( mPyramids ? tr( "pyramid creation option" ) : tr( "creation option" ), message ), QMessageBox::Close );
361  }
362  }
363 
364  if ( tmpLayer )
365  delete rasterLayer;
366 
367  return message;
368 }
369 
370 void QgsRasterFormatSaveOptionsWidget::optionsTableChanged()
371 {
372  if ( mBlockOptionUpdates )
373  return;
374 
375  QTableWidgetItem *key, *value;
376  QString options;
377  for ( int i = 0; i < mOptionsTable->rowCount(); i++ )
378  {
379  key = mOptionsTable->item( i, 0 );
380  if ( ! key || key->text().isEmpty() )
381  continue;
382  value = mOptionsTable->item( i, 1 );
383  if ( ! value || value->text().isEmpty() )
384  continue;
385  options += key->text() + '=' + value->text() + ' ';
386  }
387  options = options.trimmed();
388  mOptionsMap[ currentProfileKey()] = options;
389  mOptionsLineEdit->setText( options );
390  mOptionsLineEdit->setCursorPosition( 0 );
391 }
392 
393 void QgsRasterFormatSaveOptionsWidget::mOptionsLineEdit_editingFinished()
394 {
395  mOptionsMap[ currentProfileKey()] = mOptionsLineEdit->text().trimmed();
396 }
397 
398 void QgsRasterFormatSaveOptionsWidget::mProfileNewButton_clicked()
399 {
400  QString profileName = QInputDialog::getText( this, QString(), tr( "Profile name:" ) );
401  if ( ! profileName.isEmpty() )
402  {
403  profileName = profileName.trimmed();
404  mOptionsMap[ profileName ] = QString();
405  mProfileComboBox->addItem( profileName, profileName );
406  mProfileComboBox->setCurrentIndex( mProfileComboBox->count() - 1 );
407  }
408 }
409 
410 void QgsRasterFormatSaveOptionsWidget::mProfileDeleteButton_clicked()
411 {
412  int index = mProfileComboBox->currentIndex();
413  QString profileKey = currentProfileKey();
414  if ( index != -1 && ! sBuiltinProfiles.contains( profileKey ) )
415  {
416  mOptionsMap.remove( profileKey );
417  mProfileComboBox->removeItem( index );
418  }
419 }
420 
421 void QgsRasterFormatSaveOptionsWidget::mProfileResetButton_clicked()
422 {
423  QString profileKey = currentProfileKey();
424  if ( sBuiltinProfiles.contains( profileKey ) )
425  {
426  mOptionsMap[ profileKey ] = sBuiltinProfiles[ profileKey ][ 2 ];
427  }
428  else
429  {
430  mOptionsMap[ profileKey ] = QString();
431  }
432  mOptionsLineEdit->setText( mOptionsMap.value( currentProfileKey() ) );
433  mOptionsLineEdit->setCursorPosition( 0 );
434  updateOptions();
435 }
436 
437 void QgsRasterFormatSaveOptionsWidget::optionsTableEnableDeleteButton()
438 {
439  mOptionsDeleteButton->setEnabled( mOptionsTable->currentRow() >= 0 );
440 }
441 
442 void QgsRasterFormatSaveOptionsWidget::mOptionsAddButton_clicked()
443 {
444  mOptionsTable->insertRow( mOptionsTable->rowCount() );
445  // select the added row
446  int newRow = mOptionsTable->rowCount() - 1;
447  QTableWidgetItem *item = new QTableWidgetItem();
448  mOptionsTable->setItem( newRow, 0, item );
449  mOptionsTable->setCurrentItem( item );
450 }
451 
452 void QgsRasterFormatSaveOptionsWidget::mOptionsDeleteButton_clicked()
453 {
454  if ( mOptionsTable->currentRow() >= 0 )
455  {
456  mOptionsTable->removeRow( mOptionsTable->currentRow() );
457  // select the previous row or the next one if there is no previous row
458  QTableWidgetItem *item = mOptionsTable->item( mOptionsTable->currentRow(), 0 );
459  mOptionsTable->setCurrentItem( item );
460  optionsTableChanged();
461  }
462 }
463 
464 QString QgsRasterFormatSaveOptionsWidget::settingsKey( QString profileName ) const
465 {
466  if ( !profileName.isEmpty() )
467  profileName = "/profile_" + profileName;
468  else
469  profileName = "/profile_default" + profileName;
470  return mProvider + "/driverOptions/" + pseudoFormat().toLower() + profileName + "/create";
471 }
472 
473 QString QgsRasterFormatSaveOptionsWidget::currentProfileKey() const
474 {
475  return mProfileComboBox->currentData().toString();
476 }
477 
479 {
480 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
481  return mOptionsMap.value( currentProfileKey() ).trimmed().split( ' ', QString::SkipEmptyParts );
482 #else
483  return mOptionsMap.value( currentProfileKey() ).trimmed().split( ' ', Qt::SkipEmptyParts );
484 #endif
485 }
486 
487 QString QgsRasterFormatSaveOptionsWidget::createOptions( const QString &profileName ) const
488 {
489  QgsSettings mySettings;
490  return mySettings.value( settingsKey( profileName ), "" ).toString();
491 }
492 
493 void QgsRasterFormatSaveOptionsWidget::deleteCreateOptions( const QString &profileName )
494 {
495  QgsSettings mySettings;
496  mySettings.remove( settingsKey( profileName ) );
497 }
498 
499 void QgsRasterFormatSaveOptionsWidget::setCreateOptions()
500 {
501  QgsSettings mySettings;
502  QStringList myProfiles;
503  QMap< QString, QString >::const_iterator i = mOptionsMap.constBegin();
504  while ( i != mOptionsMap.constEnd() )
505  {
506  setCreateOptions( i.key(), i.value() );
507  myProfiles << i.key();
508  ++i;
509  }
510  mySettings.setValue( mProvider + "/driverOptions/" + pseudoFormat().toLower() + "/profiles",
511  myProfiles );
512  mySettings.setValue( mProvider + "/driverOptions/" + pseudoFormat().toLower() + "/defaultProfile",
513  currentProfileKey().trimmed() );
514 }
515 
516 void QgsRasterFormatSaveOptionsWidget::setCreateOptions( const QString &profileName, const QString &options )
517 {
518  QgsSettings mySettings;
519  mySettings.setValue( settingsKey( profileName ), options.trimmed() );
520 }
521 
522 void QgsRasterFormatSaveOptionsWidget::setCreateOptions( const QString &profileName, const QStringList &list )
523 {
524  setCreateOptions( profileName, list.join( QLatin1Char( ' ' ) ) );
525 }
526 
527 QStringList QgsRasterFormatSaveOptionsWidget::profiles() const
528 {
529  QgsSettings mySettings;
530  return mySettings.value( mProvider + "/driverOptions/" + pseudoFormat().toLower() + "/profiles", "" ).toStringList();
531 }
532 
533 void QgsRasterFormatSaveOptionsWidget::swapOptionsUI( int newIndex )
534 {
535  // set new page
536  int oldIndex;
537  if ( newIndex == -1 )
538  {
539  oldIndex = mOptionsStackedWidget->currentIndex();
540  newIndex = ( oldIndex + 1 ) % 2;
541  }
542  else
543  {
544  oldIndex = ( newIndex + 1 ) % 2;
545  }
546 
547  // resize pages to minimum - this works well with gdaltools merge ui, but not raster save as...
548  mOptionsStackedWidget->setCurrentIndex( newIndex );
549  mOptionsStackedWidget->widget( newIndex )->setSizePolicy(
550  QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ) );
551  mOptionsStackedWidget->widget( oldIndex )->setSizePolicy(
552  QSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored ) );
553  layout()->activate();
554 
555  updateOptions();
556 }
557 
558 void QgsRasterFormatSaveOptionsWidget::updateControls()
559 {
560  bool valid = mProvider == QLatin1String( "gdal" ) && !mFormat.isEmpty();
561  mOptionsValidateButton->setEnabled( valid );
562  mOptionsHelpButton->setEnabled( valid );
563 }
564 
565 // map options label left mouse click to optionsToggle()
566 bool QgsRasterFormatSaveOptionsWidget::eventFilter( QObject *obj, QEvent *event )
567 {
568  if ( event->type() == QEvent::MouseButtonPress )
569  {
570  QMouseEvent *mouseEvent = static_cast<QMouseEvent *>( event );
571  if ( mouseEvent && ( mouseEvent->button() == Qt::RightButton ) )
572  {
573  QMenu *menu = nullptr;
574  QString text;
575  if ( mOptionsStackedWidget->currentIndex() == 0 )
576  text = tr( "Use simple interface" );
577  else
578  text = tr( "Use table interface" );
579  if ( obj->objectName() == QLatin1String( "mOptionsLineEdit" ) )
580  {
581  menu = mOptionsLineEdit->createStandardContextMenu();
582  menu->addSeparator();
583  }
584  else
585  menu = new QMenu( this );
586  QAction *action = new QAction( text, menu );
587  menu->addAction( action );
588  connect( action, &QAction::triggered, this, &QgsRasterFormatSaveOptionsWidget::swapOptionsUI );
589  menu->exec( mouseEvent->globalPos() );
590  delete menu;
591  return true;
592  }
593  }
594  // standard event processing
595  return QObject::eventFilter( obj, event );
596 }
597 
599 {
600  Q_UNUSED( event )
601  mOptionsTable->horizontalHeader()->resizeSection( 0, mOptionsTable->width() - 115 );
602  QgsDebugMsg( QStringLiteral( "done" ) );
603 }
604 
605 void QgsRasterFormatSaveOptionsWidget::setOptions( const QString &options )
606 {
607  mBlockOptionUpdates++;
608  mOptionsTable->clearContents();
609 
610 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
611  const QStringList optionsList = options.trimmed().split( ' ', QString::SkipEmptyParts );
612 #else
613  const QStringList optionsList = options.trimmed().split( ' ', Qt::SkipEmptyParts );
614 #endif
615  for ( const QString &opt : optionsList )
616  {
617  int rowCount = mOptionsTable->rowCount();
618  mOptionsTable->insertRow( rowCount );
619 
620  const QStringList values = opt.split( '=' );
621  if ( values.count() == 2 )
622  {
623  QTableWidgetItem *nameItem = new QTableWidgetItem( values.at( 0 ) );
624  mOptionsTable->setItem( rowCount, 0, nameItem );
625  QTableWidgetItem *valueItem = new QTableWidgetItem( values.at( 1 ) );
626  mOptionsTable->setItem( rowCount, 1, valueItem );
627  }
628  }
629 
630  // reset to no profile index, otherwise we are changing the definition of whichever profile
631  // is currently selected...
632  mProfileComboBox->setCurrentIndex( 0 );
633 
634  mOptionsMap[ currentProfileKey()] = options.trimmed();
635  mOptionsLineEdit->setText( options.trimmed() );
636  mOptionsLineEdit->setCursorPosition( 0 );
637 
638  mBlockOptionUpdates--;
639 }
A generic dialog with layout and button box.
Definition: qgsdialog.h:34
QVBoxLayout * layout()
Returns the central layout. Widgets added to it must have this dialog as parent.
Definition: qgsdialog.h:46
static QString helpCreationOptionsFormat(const QString &format)
Gets creation options metadata for a given format.
static QString validateCreationOptionsFormat(const QStringList &createOptions, const QString &format)
Validates creation options for a given format, regardless of layer.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
virtual QString validateCreationOptions(const QStringList &createOptions, const QString &format)
Validates creation options for a specific dataset and destination format.
virtual QString validatePyramidsConfigOptions(QgsRaster::RasterPyramidsFormat pyramidsFormat, const QStringList &configOptions, const QString &fileFormat)
Validates pyramid creation options for a specific dataset and destination format.
void setFormat(const QString &format)
Set output raster format, it is used to determine list of available options.
void helpOptions()
Opens window with options description for given provider and output format.
void setOptions(const QString &options)
Populate widget with user-defined options.
void updateProfiles()
Reloads profiles list from QGIS settings.
QgsRasterFormatSaveOptionsWidget(QWidget *parent SIP_TRANSFERTHIS=nullptr, const QString &format="GTiff", QgsRasterFormatSaveOptionsWidget::Type type=Default, const QString &provider="gdal")
QString validateOptions(bool gui=true, bool reportOk=true)
Validates options correctness.
void setProvider(const QString &provider)
Set provider key, , it is used to determine list of available options.
QStringList options() const
Returns list of selected options.
void setType(QgsRasterFormatSaveOptionsWidget::Type type=Default)
Set widget look and feel.
Represents a raster layer.
int bandCount() const
Returns the number of bands in this layer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void remove(const QString &key, QgsSettings::Section section=QgsSettings::NoSection)
Removes the setting key and any sub-settings of key in a section.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Setting options for loading raster layers.