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