28#include <QColorDialog>
29#include <QInputDialog>
36#ifdef ENABLE_MODELTEST
45 mCalculatingProgressBar->hide();
46 mCancelButton->hide();
48 mContextMenu =
new QMenu( tr(
"Options" ),
this );
49 mContextMenu->addAction( tr(
"Change Color…" ),
this, SLOT( changeColor() ) );
50 mContextMenu->addAction( tr(
"Change Opacity…" ),
this, SLOT( changeOpacity() ) );
51 mContextMenu->addAction( tr(
"Change Label…" ),
this, SLOT( changeLabel() ) );
53 mAdvancedMenu =
new QMenu( tr(
"Advanced Options" ),
this );
54 QAction *mLoadFromLayerAction = mAdvancedMenu->addAction( tr(
"Load Classes from Layer" ) );
55 connect( mLoadFromLayerAction, &QAction::triggered,
this, &QgsPalettedRendererWidget::loadFromLayer );
56 QAction *loadFromFile = mAdvancedMenu->addAction( tr(
"Load Color Map from File…" ) );
57 connect( loadFromFile, &QAction::triggered,
this, &QgsPalettedRendererWidget::loadColorTable );
58 QAction *exportToFile = mAdvancedMenu->addAction( tr(
"Export Color Map to File…" ) );
59 connect( exportToFile, &QAction::triggered,
this, &QgsPalettedRendererWidget::saveColorTable );
62 mButtonAdvanced->setMenu( mAdvancedMenu );
64 mModel =
new QgsPalettedRendererModel(
this );
65 mProxyModel =
new QgsPalettedRendererProxyModel(
this );
66 mProxyModel->setSourceModel( mModel );
67 mTreeView->setSortingEnabled(
false );
68 mTreeView->setModel( mProxyModel );
72 mProxyModel->sort( QgsPalettedRendererModel::Column::ValueColumn );
75#ifdef ENABLE_MODELTEST
76 new ModelTest( mModel,
this );
79 mTreeView->setItemDelegateForColumn( QgsPalettedRendererModel::ColorColumn,
new QgsColorSwatchDelegate(
this ) );
81 mTreeView->setItemDelegateForColumn( QgsPalettedRendererModel::ValueColumn, mValueDelegate );
83 mTreeView->setColumnWidth( QgsPalettedRendererModel::ColorColumn,
Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance(
'X' ) * 6.6 );
84 mTreeView->setContextMenuPolicy( Qt::CustomContextMenu );
85 mTreeView->setSelectionMode( QAbstractItemView::ExtendedSelection );
86 mTreeView->setDragEnabled(
true );
87 mTreeView->setAcceptDrops(
true );
88 mTreeView->setDropIndicatorShown(
true );
89 mTreeView->setDragDropMode( QAbstractItemView::InternalMove );
90 mTreeView->setSelectionBehavior( QAbstractItemView::SelectRows );
91 mTreeView->setDefaultDropAction( Qt::MoveAction );
93 connect( mTreeView, &QTreeView::customContextMenuRequested,
this, [ = ]( QPoint ) { mContextMenu->exec( QCursor::pos() ); } );
95 btnColorRamp->setShowRandomColorRamp(
true );
113 connect( mDeleteEntryButton, &QPushButton::clicked,
this, &QgsPalettedRendererWidget::deleteEntry );
114 connect( mButtonDeleteAll, &QPushButton::clicked, mModel, &QgsPalettedRendererModel::deleteAll );
115 connect( mAddEntryButton, &QPushButton::clicked,
this, &QgsPalettedRendererWidget::addEntry );
116 connect( mClassifyButton, &QPushButton::clicked,
this, &QgsPalettedRendererWidget::classify );
124 mLoadFromLayerAction->setEnabled(
false );
143 int bandNumber = mBandComboBox->currentBand();
146 if ( !btnColorRamp->isNull() )
162 mModel->setClassData( pr->
classes() );
187void QgsPalettedRendererWidget::setSelectionColor(
const QItemSelection &selection,
const QColor &color )
192 QModelIndex colorIndex;
193 const auto constSelection = selection;
194 for (
const QItemSelectionRange &range : constSelection )
196 const auto constIndexes = range.indexes();
197 for (
const QModelIndex &index : constIndexes )
199 colorIndex = mModel->index( index.row(), QgsPalettedRendererModel::ColorColumn );
200 mModel->setData( colorIndex, color, Qt::EditRole );
208void QgsPalettedRendererWidget::deleteEntry()
213 QItemSelection sel = mProxyModel->mapSelectionToSource( mTreeView->selectionModel()->selection() );
214 const auto constSel = sel;
215 for (
const QItemSelectionRange &range : constSel )
217 if ( range.isValid() )
218 mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
226void QgsPalettedRendererWidget::addEntry()
230 QColor color( 150, 150, 150 );
231 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
234 color = ramp->color( 1.0 );
236 QModelIndex newEntry = mModel->addEntry( color );
237 mTreeView->scrollTo( newEntry );
238 mTreeView->selectionModel()->select( mProxyModel->mapFromSource( newEntry ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
243void QgsPalettedRendererWidget::changeColor()
245 QItemSelection sel = mProxyModel->mapSelectionToSource( mTreeView->selectionModel()->selection() );
246 QModelIndex colorIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::ColorColumn );
247 QColor currentColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
250 if ( panel && panel->dockMode() )
256 panel->openPanel( colorWidget );
262 if ( newColor.isValid() )
264 setSelectionColor( sel, newColor );
269void QgsPalettedRendererWidget::changeOpacity()
271 QItemSelection sel = mProxyModel->mapSelectionToSource( mTreeView->selectionModel()->selection() );
272 QModelIndex colorIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::ColorColumn );
273 QColor currentColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
276 double oldOpacity = ( currentColor.alpha() / 255.0 ) * 100.0;
277 double opacity = QInputDialog::getDouble(
this, tr(
"Opacity" ), tr(
"Change color opacity [%]" ), oldOpacity, 0.0, 100.0, 0, &ok );
280 int newOpacity = opacity / 100 * 255;
285 const auto constSel = sel;
286 for (
const QItemSelectionRange &range : constSel )
288 const auto constIndexes = range.indexes();
289 for (
const QModelIndex &index : constIndexes )
291 colorIndex = mModel->index( index.row(), QgsPalettedRendererModel::ColorColumn );
293 QColor newColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
294 newColor.setAlpha( newOpacity );
295 mModel->setData( colorIndex, newColor, Qt::EditRole );
304void QgsPalettedRendererWidget::changeLabel()
306 QItemSelection sel = mProxyModel->mapSelectionToSource( mTreeView->selectionModel()->selection() );
307 QModelIndex labelIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::LabelColumn );
308 QString currentLabel = mModel->data( labelIndex, Qt::DisplayRole ).toString();
311 QString newLabel = QInputDialog::getText(
this, tr(
"Label" ), tr(
"Change label" ), QLineEdit::Normal, currentLabel, &ok );
317 const auto constSel = sel;
318 for (
const QItemSelectionRange &range : constSel )
320 const auto constIndexes = range.indexes();
321 for (
const QModelIndex &index : constIndexes )
323 labelIndex = mModel->index( index.row(), QgsPalettedRendererModel::LabelColumn );
324 mModel->setData( labelIndex, newLabel, Qt::EditRole );
333void QgsPalettedRendererWidget::applyColorRamp()
335 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
344 QgsPalettedRasterRenderer::ClassData::iterator cIt = data.begin();
346 double numberOfEntries = data.count();
353 randomRamp->setTotalColorCount( numberOfEntries );
356 if ( numberOfEntries > 1 )
357 numberOfEntries -= 1;
359 for ( ; cIt != data.end(); ++cIt )
361 cIt->color = ramp->color( i / numberOfEntries );
364 mModel->setClassData( data );
370void QgsPalettedRendererWidget::loadColorTable()
373 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
374 QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Color Table from File" ), lastDir );
375 if ( !fileName.isEmpty() )
378 if ( !classes.isEmpty() )
381 mModel->setClassData( classes );
387 QMessageBox::critical(
nullptr, tr(
"Load Color Table" ), tr(
"Could not interpret file as a raster color table." ) );
392void QgsPalettedRendererWidget::saveColorTable()
395 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
396 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Color Table as File" ), lastDir, tr(
"Text (*.clr)" ) );
397 if ( !fileName.isEmpty() )
399 if ( !fileName.endsWith( QLatin1String(
".clr" ), Qt::CaseInsensitive ) )
401 fileName = fileName +
".clr";
404 QFile outputFile( fileName );
405 if ( outputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
407 QTextStream outputStream( &outputFile );
409 outputStream.flush();
412 QFileInfo fileInfo( fileName );
413 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
417 QMessageBox::warning(
this, tr(
"Save Color Table as File" ), tr(
"Write access denied. Adjust the file permissions and try again.\n\n" ) );
422void QgsPalettedRendererWidget::classify()
438 mGatherer =
new QgsPalettedRendererClassGatherer(
mRasterLayer, mBandComboBox->currentBand(), mModel->classData(), btnColorRamp->colorRamp() );
440 connect( mGatherer, &QgsPalettedRendererClassGatherer::progressChanged, mCalculatingProgressBar, [ = ](
int progress )
442 mCalculatingProgressBar->setValue( progress );
445 mCalculatingProgressBar->show();
446 mCancelButton->show();
447 connect( mCancelButton, &QPushButton::clicked, mGatherer, &QgsPalettedRendererClassGatherer::stop );
449 connect( mGatherer, &QgsPalettedRendererClassGatherer::collectedClasses,
this, &QgsPalettedRendererWidget::gatheredClasses );
450 connect( mGatherer, &QgsPalettedRendererClassGatherer::finished,
this, &QgsPalettedRendererWidget::gathererThreadFinished );
451 mClassifyButton->setText( tr(
"Calculating…" ) );
452 mClassifyButton->setEnabled(
false );
457void QgsPalettedRendererWidget::loadFromLayer()
463 QList<QgsColorRampShader::ColorRampItem> table = provider->
colorTable( mBandComboBox->currentBand() );
464 if ( !table.isEmpty() )
467 mModel->setClassData( classes );
473void QgsPalettedRendererWidget::bandChanged(
int band )
483 bool deleteExisting =
false;
484 if ( !mModel->classData().isEmpty() )
486 int res = QMessageBox::question(
this,
487 tr(
"Delete Classification" ),
488 tr(
"The classification band was changed from %1 to %2.\n"
489 "Should the existing classes be deleted?" ).arg( mBand ).arg( band ),
490 QMessageBox::Yes | QMessageBox::No );
492 deleteExisting = ( res == QMessageBox::Yes );
496 mModel->blockSignals(
true );
497 if ( deleteExisting )
500 mModel->blockSignals(
false );
504void QgsPalettedRendererWidget::gatheredClasses()
506 if ( !mGatherer || mGatherer->wasCanceled() )
509 mModel->setClassData( mGatherer->classes() );
513void QgsPalettedRendererWidget::gathererThreadFinished()
515 mGatherer->deleteLater();
517 mClassifyButton->setText( tr(
"Classify" ) );
518 mClassifyButton->setEnabled(
true );
519 mCalculatingProgressBar->hide();
520 mCancelButton->hide();
523void QgsPalettedRendererWidget::layerWillBeRemoved(
QgsMapLayer *layer )
537QgsPalettedRendererModel::QgsPalettedRendererModel( QObject *parent )
538 : QAbstractItemModel( parent )
550QModelIndex QgsPalettedRendererModel::index(
int row,
int column,
const QModelIndex &parent )
const
552 if ( column < 0 || column >= columnCount() )
555 return QModelIndex();
558 if ( !parent.isValid() && row >= 0 && row < mData.size() )
561 return createIndex( row, column );
565 return QModelIndex();
568QModelIndex QgsPalettedRendererModel::parent(
const QModelIndex &index )
const
573 return QModelIndex();
576int QgsPalettedRendererModel::columnCount(
const QModelIndex &parent )
const
578 if ( parent.isValid() )
584int QgsPalettedRendererModel::rowCount(
const QModelIndex &parent )
const
586 if ( parent.isValid() )
589 return mData.count();
592QVariant QgsPalettedRendererModel::data(
const QModelIndex &index,
int role )
const
594 if ( !index.isValid() )
599 case Qt::DisplayRole:
602 switch ( index.column() )
605 return mData.at( index.row() ).value;
608 return mData.at( index.row() ).color;
611 return mData.at( index.row() ).label;
622QVariant QgsPalettedRendererModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
624 switch ( orientation )
633 case Qt::DisplayRole:
638 return tr(
"Value" );
641 return tr(
"Color" );
644 return tr(
"Label" );
653 return QAbstractItemModel::headerData( section, orientation, role );
655 return QAbstractItemModel::headerData( section, orientation, role );
658bool QgsPalettedRendererModel::setData(
const QModelIndex &index,
const QVariant &value,
int )
660 if ( !index.isValid() )
662 if ( index.row() >= mData.length() )
665 switch ( index.column() )
670 double newValue = value.toDouble( &ok );
674 mData[ index.row() ].value = newValue;
675 emit dataChanged( index, index );
676 emit classesChanged();
682 mData[ index.row() ].color = value.value<QColor>();
683 emit dataChanged( index, index );
684 emit classesChanged();
690 mData[ index.row() ].label = value.toString();
691 emit dataChanged( index, index );
692 emit classesChanged();
700Qt::ItemFlags QgsPalettedRendererModel::flags(
const QModelIndex &index )
const
702 if ( !index.isValid() )
703 return QAbstractItemModel::flags( index ) | Qt::ItemIsDropEnabled;
705 Qt::ItemFlags f = QAbstractItemModel::flags( index );
706 switch ( index.column() )
711 f = f | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
714 return f | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
717bool QgsPalettedRendererModel::removeRows(
int row,
int count,
const QModelIndex &parent )
719 if ( row < 0 || row >= mData.count() )
721 if ( parent.isValid() )
724 for (
int i = row + count - 1; i >= row; --i )
726 beginRemoveRows( parent, i, i );
730 emit classesChanged();
734bool QgsPalettedRendererModel::insertRows(
int row,
int count,
const QModelIndex & )
736 QgsPalettedRasterRenderer::ClassData::const_iterator cIt = mData.constBegin();
737 int currentMaxValue = -std::numeric_limits<int>::max();
738 for ( ; cIt != mData.constEnd(); ++cIt )
740 int value = cIt->value;
741 currentMaxValue = std::max( value, currentMaxValue );
743 int nextValue = std::max( 0, currentMaxValue + 1 );
745 beginInsertRows( QModelIndex(), row, row + count - 1 );
746 for (
int i = row; i < row + count; ++i, ++nextValue )
751 emit classesChanged();
755Qt::DropActions QgsPalettedRendererModel::supportedDropActions()
const
757 return Qt::MoveAction;
760QStringList QgsPalettedRendererModel::mimeTypes()
const
763 types << QStringLiteral(
"application/x-qgspalettedrenderermodel" );
767QMimeData *QgsPalettedRendererModel::mimeData(
const QModelIndexList &indexes )
const
769 QMimeData *mimeData =
new QMimeData();
770 QByteArray encodedData;
772 QDataStream stream( &encodedData, QIODevice::WriteOnly );
775 const auto constIndexes = indexes;
776 for (
const QModelIndex &index : constIndexes )
778 if ( !index.isValid() || index.column() != 0 )
781 stream << index.row();
783 mimeData->setData( QStringLiteral(
"application/x-qgspalettedrenderermodel" ), encodedData );
787bool QgsPalettedRendererModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex & )
790 if ( action != Qt::MoveAction )
return true;
792 if ( !data->hasFormat( QStringLiteral(
"application/x-qgspalettedrenderermodel" ) ) )
795 QByteArray encodedData = data->data( QStringLiteral(
"application/x-qgspalettedrenderermodel" ) );
796 QDataStream stream( &encodedData, QIODevice::ReadOnly );
799 while ( !stream.atEnd() )
807 for (
int i = 0; i < rows.count(); ++i )
808 newData << mData.at( rows.at( i ) );
813 beginInsertRows( QModelIndex(), row, row + rows.count() - 1 );
814 for (
int i = 0; i < rows.count(); ++i )
815 mData.insert( row + i, newData.at( i ) );
817 emit classesChanged();
821QModelIndex QgsPalettedRendererModel::addEntry(
const QColor &color )
823 insertRow( rowCount() );
824 QModelIndex newRow = index( mData.count() - 1, 1 );
825 setData( newRow, color );
829void QgsPalettedRendererModel::deleteAll()
834 emit classesChanged();
842 : mProvider( ( layer && layer->dataProvider() ) ? layer->dataProvider()->clone() : nullptr )
843 , mBandNumber( bandNumber )
845 , mClasses( existingClasses )
846 , mWasCanceled( false )
849void QgsPalettedRendererClassGatherer::run()
851 mWasCanceled =
false;
862 QgsPalettedRasterRenderer::ClassData::iterator classIt = newClasses.begin();
863 emit progressChanged( 0 );
865 for ( ; classIt != newClasses.end(); ++classIt )
870 if ( existingClass.value == classIt->value )
872 classIt->color = existingClass.color;
873 classIt->label = existingClass.label;
878 emit progressChanged( 100 * (
static_cast< double >( i ) /
static_cast<double>( newClasses.count() ) ) );
880 mClasses = newClasses;
884 mFeedbackMutex.lock();
887 mFeedbackMutex.unlock();
889 emit collectedClasses();
896 for (
int i = 0; i < rowCount( ); ++i )
898 data.push_back( qobject_cast<QgsPalettedRendererModel *>( sourceModel() )->classAtIndex( mapToSource( index( i, 0 ) ) ) );
@ UnknownDataType
Unknown or unspecified type.
static const double UI_SCALE_FACTOR
UI scaling factor.
static QColor getColor(const QColor &initialColor, QWidget *parent, const QString &title=QString(), bool allowOpacity=false)
Returns a color selection from a color dialog.
Abstract base class for color ramps.
A delegate for showing a color swatch in a list.
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
Base class for all map layer types.
Renderer for paletted raster images.
int band() const
Returns the raster band used for rendering the raster.
QgsColorRamp * sourceColorRamp() const
Gets the source color ramp.
void setSourceColorRamp(QgsColorRamp *ramp)
Set the source color ramp.
QList< QgsPalettedRasterRenderer::Class > ClassData
Map of value to class properties.
static QgsPalettedRasterRenderer::ClassData classDataFromFile(const QString &path)
Opens a color table file and returns corresponding paletted renderer class data.
static QgsPalettedRasterRenderer::ClassData colorTableToClassData(const QList< QgsColorRampShader::ColorRampItem > &table)
Converts a raster color table to paletted renderer class data.
ClassData classes() const
Returns a map of value to classes (colors) used by the renderer.
static QgsPalettedRasterRenderer::ClassData classDataFromRaster(QgsRasterInterface *raster, int bandNumber, QgsColorRamp *ramp=nullptr, QgsRasterBlockFeedback *feedback=nullptr)
Generates class data from a raster, for the specified bandNumber.
static QString classDataToString(const QgsPalettedRasterRenderer::ClassData &classes)
Converts classes to a string representation, using the .clr/gdal color table file format.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
static QgsProject * instance()
Returns the QgsProject singleton instance.
void layerWillBeRemoved(const QString &layerId)
Emitted when a layer is about to be removed from the registry.
Totally random color ramp.
void bandChanged(int band)
Emitted when the currently selected band changes.
Feedback object tailored for raster block reading.
Base class for raster data providers.
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
virtual QList< QgsColorRampShader::ColorRampItem > colorTable(int bandNo) const
Represents a raster layer.
QgsRasterRenderer * renderer() const
Returns the raster's renderer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
Raster renderer pipe that applies colors to a raster.
A rectangle specified with double values.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Properties of a single value class.