34 #include <QPushButton>
35 #include <QInputDialog>
36 #include <QFileDialog>
38 #include <QMessageBox>
39 #include <QTextStream>
48 mLoadFromBandButton->setVisible(
false );
50 connect( mAddEntryButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mAddEntryButton_clicked );
51 connect( mDeleteEntryButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mDeleteEntryButton_clicked );
52 connect( mLoadFromBandButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mLoadFromBandButton_clicked );
53 connect( mLoadFromFileButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mLoadFromFileButton_clicked );
54 connect( mExportToFileButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mExportToFileButton_clicked );
55 connect( mUnitLineEdit, &QLineEdit::textEdited,
this, &QgsColorRampShaderWidget::mUnitLineEdit_textEdited );
56 connect( mColormapTreeWidget, &QTreeWidget::itemDoubleClicked,
this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemDoubleClicked );
57 connect( mColorInterpolationComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsColorRampShaderWidget::mColorInterpolationComboBox_currentIndexChanged );
58 connect( mClassificationModeComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsColorRampShaderWidget::mClassificationModeComboBox_currentIndexChanged );
60 contextMenu =
new QMenu( tr(
"Options" ),
this );
61 contextMenu->addAction( tr(
"Change Color…" ),
this, SLOT( changeColor() ) );
62 contextMenu->addAction( tr(
"Change Opacity…" ),
this, SLOT( changeOpacity() ) );
65 mColormapTreeWidget->setItemDelegateForColumn( ColorColumn, mSwatchDelegate );
67 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
68 mColormapTreeWidget->setColumnWidth( ColorColumn,
Qgis::UI_SCALE_FACTOR * fontMetrics().width(
'X' ) * 6.6 );
70 mColormapTreeWidget->setColumnWidth( ColorColumn,
Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance(
'X' ) * 6.6 );
73 mColormapTreeWidget->setContextMenuPolicy( Qt::CustomContextMenu );
74 mColormapTreeWidget->setSelectionMode( QAbstractItemView::ExtendedSelection );
75 connect( mColormapTreeWidget, &QTreeView::customContextMenuRequested,
this, [ = ]( QPoint ) { contextMenu->exec( QCursor::pos() ); } );
77 QString defaultPalette = settings.
value( QStringLiteral(
"Raster/defaultPalette" ),
"" ).toString();
78 btnColorRamp->setColorRampFromName( defaultPalette );
90 mNumberOfEntriesSpinBox->setValue( 5 );
92 mClassificationModeComboBox_currentIndexChanged( 0 );
94 resetClassifyButton();
112 mRasterDataProvider = dp;
113 mLoadFromBandButton->setVisible(
bool( mRasterDataProvider ) );
131 colorRampShader.
setClip( mClipCheckBox->isChecked() );
134 QList<QgsColorRampShader::ColorRampItem> colorRampItems;
135 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
136 QTreeWidgetItem *currentItem =
nullptr;
137 for (
int i = 0; i < topLevelItemCount; ++i )
139 currentItem = mColormapTreeWidget->topLevelItem( i );
145 newColorRampItem.
value = currentItem->text( ValueColumn ).toDouble();
146 newColorRampItem.
color = currentItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
147 newColorRampItem.
label = currentItem->text( LabelColumn );
148 colorRampItems.append( newColorRampItem );
151 std::sort( colorRampItems.begin(), colorRampItems.end() );
154 if ( !btnColorRamp->isNull() )
158 return colorRampShader;
161 void QgsColorRampShaderWidget::autoLabel()
165 QString unit = mUnitLineEdit->text();
167 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
168 QTreeWidgetItem *currentItem =
nullptr;
169 for (
int i = 0; i < topLevelItemCount; ++i )
171 currentItem = mColormapTreeWidget->topLevelItem( i );
173 if ( !currentItem || currentItem->text( ValueColumn ).isEmpty() )
182 label =
"<= " + currentItem->text( ValueColumn ) + unit;
184 else if ( currentItem->text( ValueColumn ).toDouble() == std::numeric_limits<double>::infinity() )
186 label =
"> " + mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) + unit;
190 label = mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) +
" - " + currentItem->text( ValueColumn ) + unit;
195 label = currentItem->text( ValueColumn ) + unit;
198 if ( currentItem->text( LabelColumn ).isEmpty() || currentItem->text( LabelColumn ) == label || currentItem->foreground( LabelColumn ).color() == QColor( Qt::gray ) )
200 currentItem->setText( LabelColumn, label );
201 currentItem->setForeground( LabelColumn, QBrush( QColor( Qt::gray ) ) );
206 void QgsColorRampShaderWidget::setUnitFromLabels()
210 QStringList allSuffixes;
212 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
213 QTreeWidgetItem *currentItem =
nullptr;
214 for (
int i = 0; i < topLevelItemCount; ++i )
216 currentItem = mColormapTreeWidget->topLevelItem( i );
218 if ( !currentItem || currentItem->text( ValueColumn ).isEmpty() )
227 label =
"<= " + currentItem->text( ValueColumn );
229 else if ( currentItem->text( ValueColumn ).toDouble() == std::numeric_limits<double>::infinity() )
231 label =
"> " + mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn );
235 label = mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) +
" - " + currentItem->text( ValueColumn );
240 label = currentItem->text( ValueColumn );
243 if ( currentItem->text( LabelColumn ).startsWith( label ) )
245 allSuffixes.append( currentItem->text( LabelColumn ).mid( label.length() ) );
249 QStringList suffixes = QStringList( allSuffixes );
250 suffixes.removeDuplicates();
253 for (
int i = 0; i < suffixes.count(); ++i )
255 int n = allSuffixes.count( suffixes[i] );
265 mUnitLineEdit->setText( unit );
269 void QgsColorRampShaderWidget::mAddEntryButton_clicked()
272 newItem->setText( ValueColumn, QStringLiteral(
"0" ) );
273 newItem->
setData( ColorColumn, Qt::EditRole, QColor( Qt::magenta ) );
274 newItem->setText( LabelColumn, QString() );
275 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
277 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
278 mColormapTreeWidget->sortItems( ValueColumn, Qt::AscendingOrder );
285 void QgsColorRampShaderWidget::mDeleteEntryButton_clicked()
287 QList<QTreeWidgetItem *> itemList;
288 itemList = mColormapTreeWidget->selectedItems();
289 if ( itemList.isEmpty() )
294 const auto constItemList = itemList;
295 for ( QTreeWidgetItem *item : constItemList )
306 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
307 if ( !ramp || std::isnan( mMin ) || std::isnan( mMax ) )
320 colorRampShader->classifyColorRamp( mNumberOfEntriesSpinBox->value(),
323 mRasterDataProvider );
324 colorRampShader->setClip( mClipCheckBox->isChecked() );
326 mColormapTreeWidget->clear();
328 const QList<QgsColorRampShader::ColorRampItem> colorRampItemList = colorRampShader->colorRampItemList();
329 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItemList.constBegin();
330 for ( ; it != colorRampItemList.end(); ++it )
333 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
334 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
335 newItem->setText( LabelColumn, QString() );
336 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
338 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
341 mClipCheckBox->setChecked( colorRampShader->clip() );
347 void QgsColorRampShaderWidget::mClassificationModeComboBox_currentIndexChanged(
int index )
354 void QgsColorRampShaderWidget::applyColorRamp()
356 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
362 if ( !btnColorRamp->colorRampName().isEmpty() )
366 settings.
setValue( QStringLiteral(
"Raster/defaultPalette" ), btnColorRamp->colorRampName() );
369 bool enableContinuous = ( ramp->count() > 0 );
370 mClassificationModeComboBox->setEnabled( enableContinuous );
371 if ( !enableContinuous )
376 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
377 if ( topLevelItemCount > 0 )
380 QTreeWidgetItem *currentItem =
nullptr;
381 for (
int i = 0; i < topLevelItemCount; ++i )
383 currentItem = mColormapTreeWidget->topLevelItem( i );
389 double value = currentItem->text( ValueColumn ).toDouble();
390 double position = ( value - mMin ) / ( mMax - mMin );
404 mColormapTreeWidget->clear();
405 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItems.constBegin();
406 for ( ; it != colorRampItems.constEnd(); ++it )
409 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
410 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
411 newItem->setText( LabelColumn, it->label );
412 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
414 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
419 void QgsColorRampShaderWidget::mLoadFromBandButton_clicked()
421 if ( !mRasterDataProvider )
424 QList<QgsColorRampShader::ColorRampItem> colorRampList = mRasterDataProvider->
colorTable( mBand );
425 if ( !colorRampList.isEmpty() )
432 QMessageBox::warning(
this, tr(
"Load Color Map" ), tr(
"The color map for band %1 has no entries." ).arg( mBand ) );
438 void QgsColorRampShaderWidget::mLoadFromFileButton_clicked()
441 bool importError =
false;
444 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
445 QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Color Map from File" ), lastDir, tr(
"Textfile (*.txt)" ) );
446 QFile inputFile( fileName );
447 if ( inputFile.open( QFile::ReadOnly ) )
450 mColormapTreeWidget->clear();
452 QTextStream inputStream( &inputFile );
454 QStringList inputStringComponents;
455 QList<QgsColorRampShader::ColorRampItem> colorRampItems;
458 while ( !inputStream.atEnd() )
461 inputLine = inputStream.readLine();
462 if ( !inputLine.isEmpty() )
464 if ( !inputLine.simplified().startsWith(
'#' ) )
466 if ( inputLine.contains( QLatin1String(
"INTERPOLATION" ), Qt::CaseInsensitive ) )
468 inputStringComponents = inputLine.split(
':' );
469 if ( inputStringComponents.size() == 2 )
471 if ( inputStringComponents[1].trimmed().toUpper().compare( QLatin1String(
"INTERPOLATED" ), Qt::CaseInsensitive ) == 0 )
475 else if ( inputStringComponents[1].trimmed().toUpper().compare( QLatin1String(
"DISCRETE" ), Qt::CaseInsensitive ) == 0 )
487 badLines = badLines + QString::number( lineCounter ) +
":\t[" + inputLine +
"]\n";
492 inputStringComponents = inputLine.split(
',' );
493 if ( inputStringComponents.size() == 6 )
496 QColor::fromRgb( inputStringComponents[1].toInt(), inputStringComponents[2].toInt(),
497 inputStringComponents[3].toInt(), inputStringComponents[4].toInt() ),
498 inputStringComponents[5] );
499 colorRampItems.push_back( currentItem );
504 badLines = badLines + QString::number( lineCounter ) +
":\t[" + inputLine +
"]\n";
513 QFileInfo fileInfo( fileName );
514 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
518 QMessageBox::warning(
this, tr(
"Load Color Map from File" ), tr(
"The following lines contained errors\n\n" ) + badLines );
521 else if ( !fileName.isEmpty() )
523 QMessageBox::warning(
this, tr(
"Load Color Map from File" ), tr(
"Read access denied. Adjust the file permissions and try again.\n\n" ) );
530 void QgsColorRampShaderWidget::mExportToFileButton_clicked()
533 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
534 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Color Map as File" ), lastDir, tr(
"Textfile (*.txt)" ) );
535 if ( !fileName.isEmpty() )
537 if ( !fileName.endsWith( QLatin1String(
".txt" ), Qt::CaseInsensitive ) )
539 fileName = fileName +
".txt";
542 QFile outputFile( fileName );
543 if ( outputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
545 QTextStream outputStream( &outputFile );
546 outputStream <<
"# " << tr(
"QGIS Generated Color Map Export File" ) <<
'\n';
547 outputStream <<
"INTERPOLATION:";
549 switch ( interpolation )
552 outputStream <<
"INTERPOLATED\n";
555 outputStream <<
"DISCRETE\n";
558 outputStream <<
"EXACT\n";
562 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
563 QTreeWidgetItem *currentItem =
nullptr;
565 for (
int i = 0; i < topLevelItemCount; ++i )
567 currentItem = mColormapTreeWidget->topLevelItem( i );
572 color = currentItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
573 outputStream << currentItem->text( ValueColumn ).toDouble() <<
',';
574 outputStream << color.red() <<
',' << color.green() <<
',' << color.blue() <<
',' << color.alpha() <<
',';
575 if ( currentItem->text( LabelColumn ).isEmpty() )
577 outputStream <<
"Color entry " << i + 1 <<
'\n';
581 outputStream << currentItem->text( LabelColumn ) <<
'\n';
584 outputStream.flush();
587 QFileInfo fileInfo( fileName );
588 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
592 QMessageBox::warning(
this, tr(
"Save Color Map as File" ), tr(
"Write access denied. Adjust the file permissions and try again.\n\n" ) );
597 void QgsColorRampShaderWidget::mColormapTreeWidget_itemDoubleClicked( QTreeWidgetItem *item,
int column )
604 if ( column == LabelColumn )
607 item->setForeground( LabelColumn, QBrush() );
611 void QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited( QTreeWidgetItem *item,
int column )
619 mColormapTreeWidget->sortItems( ValueColumn, Qt::AscendingOrder );
652 whileBlocking( mColorInterpolationComboBox )->setCurrentIndex( mColorInterpolationComboBox->findData( colorRampShader.
colorRampType() ) );
653 mColorInterpolationComboBox_currentIndexChanged( mColorInterpolationComboBox->currentIndex() );
655 mClassificationModeComboBox_currentIndexChanged( mClassificationModeComboBox->currentIndex() );
665 QString defaultPalette = settings.
value( QStringLiteral(
"/Raster/defaultPalette" ),
"Spectral" ).toString();
666 btnColorRamp->setColorRampFromName( defaultPalette );
672 void QgsColorRampShaderWidget::mColorInterpolationComboBox_currentIndexChanged(
int index )
679 QString valueToolTip;
680 switch ( interpolation )
683 valueLabel = tr(
"Value" );
684 valueToolTip = tr(
"Value for color stop" );
687 valueLabel = tr(
"Value <=" );
688 valueToolTip = tr(
"Maximum value for class" );
691 valueLabel = tr(
"Value =" );
692 valueToolTip = tr(
"Value for color" );
696 QTreeWidgetItem *header = mColormapTreeWidget->headerItem();
697 header->setText( ValueColumn, valueLabel );
698 header->setToolTip( ValueColumn, valueToolTip );
717 resetClassifyButton();
734 QTreeWidgetItem *item = mColormapTreeWidget->topLevelItem( 0 );
740 double min = item->text( ValueColumn ).toDouble();
741 item = mColormapTreeWidget->topLevelItem( mColormapTreeWidget->topLevelItemCount() - 1 );
742 double max = item->text( ValueColumn ).toDouble();
752 void QgsColorRampShaderWidget::resetClassifyButton()
754 mClassifyButton->setEnabled(
true );
755 if ( std::isnan( mMin ) || std::isnan( mMax ) || mMin >= mMax )
757 mClassifyButton->setEnabled(
false );
761 void QgsColorRampShaderWidget::changeColor()
763 QList<QTreeWidgetItem *> itemList;
764 itemList = mColormapTreeWidget->selectedItems();
765 if ( itemList.isEmpty() )
769 QTreeWidgetItem *firstItem = itemList.first();
771 QColor currentColor = firstItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
773 if ( panel && panel->dockMode() )
780 for ( QTreeWidgetItem *item : qgis::as_const( itemList ) )
782 item->setData( ColorColumn, Qt::EditRole, newColor );
788 panel->openPanel( colorWidget );
794 if ( newColor.isValid() )
796 for ( QTreeWidgetItem *item : qgis::as_const( itemList ) )
798 item->setData( ColorColumn, Qt::EditRole, newColor );
807 void QgsColorRampShaderWidget::changeOpacity()
809 QList<QTreeWidgetItem *> itemList;
810 itemList = mColormapTreeWidget->selectedItems();
811 if ( itemList.isEmpty() )
815 QTreeWidgetItem *firstItem = itemList.first();
818 double oldOpacity = firstItem->data( ColorColumn, Qt::EditRole ).value<QColor>().alpha() / 255 * 100;
819 double opacity = QInputDialog::getDouble(
this, tr(
"Opacity" ), tr(
"Change color opacity [%]" ), oldOpacity, 0.0, 100.0, 0, &ok );
822 int newOpacity =
static_cast<int>( opacity / 100 * 255 );
823 const auto constItemList = itemList;
824 for ( QTreeWidgetItem *item : constItemList )
826 QColor newColor = item->data( ColorColumn, Qt::EditRole ).value<QColor>();
827 newColor.setAlpha( newOpacity );
828 item->setData( ColorColumn, Qt::EditRole, newColor );