37 #include <QPushButton>
38 #include <QInputDialog>
39 #include <QFileDialog>
41 #include <QMessageBox>
42 #include <QTextStream>
51 mLoadFromBandButton->setVisible(
false );
53 connect( mAddEntryButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mAddEntryButton_clicked );
54 connect( mDeleteEntryButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mDeleteEntryButton_clicked );
55 connect( mLoadFromBandButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mLoadFromBandButton_clicked );
56 connect( mLoadFromFileButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mLoadFromFileButton_clicked );
57 connect( mExportToFileButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mExportToFileButton_clicked );
58 connect( mUnitLineEdit, &QLineEdit::textEdited,
this, &QgsColorRampShaderWidget::mUnitLineEdit_textEdited );
59 connect( mColormapTreeWidget, &QTreeWidget::itemDoubleClicked,
this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemDoubleClicked );
60 connect( mColorInterpolationComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsColorRampShaderWidget::mColorInterpolationComboBox_currentIndexChanged );
61 connect( mClassificationModeComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsColorRampShaderWidget::mClassificationModeComboBox_currentIndexChanged );
63 contextMenu =
new QMenu( tr(
"Options" ),
this );
64 contextMenu->addAction( tr(
"Change Color…" ),
this, SLOT( changeColor() ) );
65 contextMenu->addAction( tr(
"Change Opacity…" ),
this, SLOT( changeOpacity() ) );
68 mColormapTreeWidget->setItemDelegateForColumn( ColorColumn, mSwatchDelegate );
70 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
71 mColormapTreeWidget->setColumnWidth( ColorColumn,
Qgis::UI_SCALE_FACTOR * fontMetrics().width(
'X' ) * 6.6 );
73 mColormapTreeWidget->setColumnWidth( ColorColumn,
Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance(
'X' ) * 6.6 );
76 mColormapTreeWidget->setContextMenuPolicy( Qt::CustomContextMenu );
77 mColormapTreeWidget->setSelectionMode( QAbstractItemView::ExtendedSelection );
78 connect( mColormapTreeWidget, &QTreeView::customContextMenuRequested,
this, [ = ]( QPoint ) { contextMenu->exec( QCursor::pos() ); } );
80 QString defaultPalette = settings.
value( QStringLiteral(
"Raster/defaultPalette" ),
"" ).toString();
81 btnColorRamp->setColorRampFromName( defaultPalette );
93 mNumberOfEntriesSpinBox->setValue( 5 );
95 mClassificationModeComboBox_currentIndexChanged( 0 );
97 resetClassifyButton();
105 connect( mLabelPrecisionSpinBox, qgis::overload<int>::of( &QSpinBox::valueChanged ),
this, [ = ](
int )
119 mRasterDataProvider = dp;
120 mLoadFromBandButton->setVisible(
bool( mRasterDataProvider ) );
127 if ( mRasterDataProvider )
130 mLabelPrecisionSpinBox->setMaximum( maxDigits );
144 colorRampShader.
setClip( mClipCheckBox->isChecked() );
147 QList<QgsColorRampShader::ColorRampItem> colorRampItems;
148 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
149 QTreeWidgetItem *currentItem =
nullptr;
150 for (
int i = 0; i < topLevelItemCount; ++i )
152 currentItem = mColormapTreeWidget->topLevelItem( i );
158 newColorRampItem.
value = QLocale().toDouble( currentItem->text( ValueColumn ) );
159 newColorRampItem.
color = currentItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
160 newColorRampItem.
label = currentItem->text( LabelColumn );
161 colorRampItems.append( newColorRampItem );
164 std::sort( colorRampItems.begin(), colorRampItems.end() );
167 if ( !btnColorRamp->isNull() )
171 return colorRampShader;
174 void QgsColorRampShaderWidget::autoLabel()
178 QString unit = mUnitLineEdit->text();
180 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
182 auto applyPrecision = [ = ](
const QString & value )
184 double val { QLocale().toDouble( value ) };
185 if ( mLabelPrecisionSpinBox->value() < 0 )
187 const double factor = std::pow( 10, - mLabelPrecisionSpinBox->value() );
188 val =
static_cast<qlonglong
>( val / factor ) * factor;
189 return QLocale().toString( val,
'f', 0 );
191 return QLocale().toString( val,
'f', mLabelPrecisionSpinBox->value() );
194 QTreeWidgetItem *currentItem =
nullptr;
195 for (
int i = 0; i < topLevelItemCount; ++i )
197 currentItem = mColormapTreeWidget->topLevelItem( i );
199 if ( !currentItem || currentItem->text( ValueColumn ).isEmpty() )
208 label =
"<= " + applyPrecision( currentItem->text( ValueColumn ) ) + unit;
210 else if ( QLocale().toDouble( currentItem->text( ValueColumn ) ) == std::numeric_limits<double>::infinity() )
212 label =
"> " + applyPrecision( mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) ) + unit;
216 label = applyPrecision( mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) ) +
" - " + applyPrecision( currentItem->text( ValueColumn ) ) + unit;
221 label = applyPrecision( currentItem->text( ValueColumn ) ) + unit;
224 if ( currentItem->text( LabelColumn ).isEmpty() || currentItem->text( LabelColumn ) == label || currentItem->foreground( LabelColumn ).color() == QColor( Qt::gray ) )
226 currentItem->setText( LabelColumn, label );
227 currentItem->setForeground( LabelColumn, QBrush( QColor( Qt::gray ) ) );
232 void QgsColorRampShaderWidget::setUnitFromLabels()
236 QStringList allSuffixes;
238 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
239 QTreeWidgetItem *currentItem =
nullptr;
240 for (
int i = 0; i < topLevelItemCount; ++i )
242 currentItem = mColormapTreeWidget->topLevelItem( i );
244 if ( !currentItem || currentItem->text( ValueColumn ).isEmpty() )
253 label =
"<= " + currentItem->text( ValueColumn );
255 else if ( currentItem->text( ValueColumn ).toDouble() == std::numeric_limits<double>::infinity() )
257 label =
"> " + mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn );
261 label = mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) +
" - " + currentItem->text( ValueColumn );
266 label = currentItem->text( ValueColumn );
269 if ( currentItem->text( LabelColumn ).startsWith( label ) )
271 allSuffixes.append( currentItem->text( LabelColumn ).mid( label.length() ) );
275 QStringList suffixes = QStringList( allSuffixes );
276 suffixes.removeDuplicates();
279 for (
int i = 0; i < suffixes.count(); ++i )
281 int n = allSuffixes.count( suffixes[i] );
291 mUnitLineEdit->setText( unit );
295 void QgsColorRampShaderWidget::mAddEntryButton_clicked()
298 newItem->setText( ValueColumn, QStringLiteral(
"0" ) );
299 newItem->
setData( ColorColumn, Qt::EditRole, QColor( Qt::magenta ) );
300 newItem->setText( LabelColumn, QString() );
301 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
303 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
304 mColormapTreeWidget->sortItems( ValueColumn, Qt::AscendingOrder );
311 void QgsColorRampShaderWidget::mDeleteEntryButton_clicked()
313 QList<QTreeWidgetItem *> itemList;
314 itemList = mColormapTreeWidget->selectedItems();
315 if ( itemList.isEmpty() )
320 const auto constItemList = itemList;
321 for ( QTreeWidgetItem *item : constItemList )
332 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
333 if ( !ramp || std::isnan( mMin ) || std::isnan( mMax ) )
346 colorRampShader->classifyColorRamp( mNumberOfEntriesSpinBox->value(),
349 mRasterDataProvider );
350 colorRampShader->setClip( mClipCheckBox->isChecked() );
352 mColormapTreeWidget->clear();
354 const QList<QgsColorRampShader::ColorRampItem> colorRampItemList = colorRampShader->colorRampItemList();
355 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItemList.constBegin();
356 for ( ; it != colorRampItemList.end(); ++it )
359 newItem->setText( ValueColumn, QLocale().toString( it->value,
'g', 15 ) );
360 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
361 newItem->setText( LabelColumn, QString() );
362 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
364 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
367 mClipCheckBox->setChecked( colorRampShader->clip() );
373 void QgsColorRampShaderWidget::mClassificationModeComboBox_currentIndexChanged(
int index )
380 void QgsColorRampShaderWidget::applyColorRamp()
382 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
388 if ( !btnColorRamp->colorRampName().isEmpty() )
392 settings.
setValue( QStringLiteral(
"Raster/defaultPalette" ), btnColorRamp->colorRampName() );
395 bool enableContinuous = ( ramp->count() > 0 );
396 mClassificationModeComboBox->setEnabled( enableContinuous );
397 if ( !enableContinuous )
402 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
403 if ( topLevelItemCount > 0 )
407 if ( std::isnan( mMin ) || std::isnan( mMax ) )
409 colormapMinMax( min, max );
418 QTreeWidgetItem *currentItem =
nullptr;
419 for (
int i = 0; i < topLevelItemCount; ++i )
421 currentItem = mColormapTreeWidget->topLevelItem( i );
427 double value = QLocale().toDouble( currentItem->text( ValueColumn ) );
428 double position = ( value - min ) / ( max - min );
442 mColormapTreeWidget->clear();
443 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItems.constBegin();
444 for ( ; it != colorRampItems.constEnd(); ++it )
447 newItem->setText( ValueColumn, QLocale().toString( it->value,
'g', 15 ) );
448 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
449 newItem->setText( LabelColumn, it->label );
450 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
452 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
457 void QgsColorRampShaderWidget::mLoadFromBandButton_clicked()
459 if ( !mRasterDataProvider )
462 QList<QgsColorRampShader::ColorRampItem> colorRampList = mRasterDataProvider->
colorTable( mBand );
463 if ( !colorRampList.isEmpty() )
470 QMessageBox::warning(
this, tr(
"Load Color Map" ), tr(
"The color map for band %1 has no entries." ).arg( mBand ) );
476 void QgsColorRampShaderWidget::mLoadFromFileButton_clicked()
479 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
480 const QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Color Map from File" ), lastDir, tr(
"Textfile (*.txt)" ) );
481 if ( fileName.isEmpty() )
484 QList<QgsColorRampShader::ColorRampItem> colorRampItems;
490 mColormapTreeWidget->clear();
492 mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findData( type ) );
496 if ( !errors.empty() )
498 QMessageBox::warning(
this, tr(
"Load Color Map from File" ), tr(
"The following lines contained errors\n\n" ) + errors.join(
'\n' ) );
503 const QString error = tr(
"An error occurred while reading the color map\n\n" ) + errors.join(
'\n' );
504 QMessageBox::warning(
this, tr(
"Load Color Map from File" ), error );
507 QFileInfo fileInfo( fileName );
508 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
514 void QgsColorRampShaderWidget::mExportToFileButton_clicked()
517 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
518 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Color Map as File" ), lastDir, tr(
"Textfile (*.txt)" ) );
519 if ( fileName.isEmpty() )
524 QList<QgsColorRampShader::ColorRampItem> colorRampItems;
525 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
526 for (
int i = 0; i < topLevelItemCount; ++i )
528 QTreeWidgetItem *currentItem = mColormapTreeWidget->topLevelItem( i );
535 item.
value = QLocale().toDouble( currentItem->text( ValueColumn ) );
536 item.
color = currentItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
537 item.
label = currentItem->text( LabelColumn );
538 colorRampItems << item;
543 QMessageBox::warning(
this, tr(
"Save Color Map as File" ), tr(
"Write access denied. Adjust the file permissions and try again.\n\n" ) );
546 QFileInfo fileInfo( fileName );
547 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
550 void QgsColorRampShaderWidget::mColormapTreeWidget_itemDoubleClicked( QTreeWidgetItem *item,
int column )
557 if ( column == LabelColumn )
560 item->setForeground( LabelColumn, QBrush() );
564 void QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited( QTreeWidgetItem *item,
int column )
572 mColormapTreeWidget->sortItems( ValueColumn, Qt::AscendingOrder );
603 whileBlocking( mColorInterpolationComboBox )->setCurrentIndex( mColorInterpolationComboBox->findData( colorRampShader.
colorRampType() ) );
604 mColorInterpolationComboBox_currentIndexChanged( mColorInterpolationComboBox->currentIndex() );
606 mClassificationModeComboBox_currentIndexChanged( mClassificationModeComboBox->currentIndex() );
616 QString defaultPalette = settings.
value( QStringLiteral(
"/Raster/defaultPalette" ),
"Spectral" ).toString();
617 btnColorRamp->setColorRampFromName( defaultPalette );
625 void QgsColorRampShaderWidget::mColorInterpolationComboBox_currentIndexChanged(
int index )
632 QString valueToolTip;
633 switch ( interpolation )
636 valueLabel = tr(
"Value" );
637 valueToolTip = tr(
"Value for color stop" );
640 valueLabel = tr(
"Value <=" );
641 valueToolTip = tr(
"Maximum value for class" );
644 valueLabel = tr(
"Value =" );
645 valueToolTip = tr(
"Value for color" );
649 QTreeWidgetItem *header = mColormapTreeWidget->headerItem();
650 header->setText( ValueColumn, valueLabel );
651 header->setToolTip( ValueColumn, valueToolTip );
670 resetClassifyButton();
683 bool QgsColorRampShaderWidget::colormapMinMax(
double &min,
double &max )
const
685 QTreeWidgetItem *item = mColormapTreeWidget->topLevelItem( 0 );
691 min = QLocale().toDouble( item->text( ValueColumn ) );
692 item = mColormapTreeWidget->topLevelItem( mColormapTreeWidget->topLevelItemCount() - 1 );
693 max = QLocale().toDouble( item->text( ValueColumn ) );
700 double min = 0, max = 0;
701 if ( ! colormapMinMax( min, max ) )
714 void QgsColorRampShaderWidget::resetClassifyButton()
716 mClassifyButton->setEnabled(
true );
717 if ( std::isnan( mMin ) || std::isnan( mMax ) || mMin >= mMax )
719 mClassifyButton->setEnabled(
false );
723 void QgsColorRampShaderWidget::changeColor()
725 QList<QTreeWidgetItem *> itemList;
726 itemList = mColormapTreeWidget->selectedItems();
727 if ( itemList.isEmpty() )
731 QTreeWidgetItem *firstItem = itemList.first();
733 QColor currentColor = firstItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
735 if ( panel && panel->dockMode() )
742 for ( QTreeWidgetItem *item : qgis::as_const( itemList ) )
744 item->setData( ColorColumn, Qt::EditRole, newColor );
750 panel->openPanel( colorWidget );
756 if ( newColor.isValid() )
758 for ( QTreeWidgetItem *item : qgis::as_const( itemList ) )
760 item->setData( ColorColumn, Qt::EditRole, newColor );
769 void QgsColorRampShaderWidget::changeOpacity()
771 QList<QTreeWidgetItem *> itemList;
772 itemList = mColormapTreeWidget->selectedItems();
773 if ( itemList.isEmpty() )
777 QTreeWidgetItem *firstItem = itemList.first();
780 double oldOpacity = firstItem->data( ColorColumn, Qt::EditRole ).value<QColor>().alpha() / 255 * 100;
781 double opacity = QInputDialog::getDouble(
this, tr(
"Opacity" ), tr(
"Change color opacity [%]" ), oldOpacity, 0.0, 100.0, 0, &ok );
784 int newOpacity =
static_cast<int>( opacity / 100 * 255 );
785 const auto constItemList = itemList;
786 for ( QTreeWidgetItem *item : constItemList )
788 QColor newColor = item->data( ColorColumn, Qt::EditRole ).value<QColor>();
789 newColor.setAlpha( newOpacity );
790 item->setData( ColorColumn, Qt::EditRole, newColor );