29#include <QColorDialog>
30#include <QInputDialog>
37#ifdef ENABLE_MODELTEST
46 mCalculatingProgressBar->hide();
47 mCancelButton->hide();
49 mContextMenu =
new QMenu( tr(
"Options" ),
this );
50 mContextMenu->addAction( tr(
"Change Color…" ),
this, SLOT( changeColor() ) );
51 mContextMenu->addAction( tr(
"Change Opacity…" ),
this, SLOT( changeOpacity() ) );
52 mContextMenu->addAction( tr(
"Change Label…" ),
this, SLOT( changeLabel() ) );
54 mAdvancedMenu =
new QMenu( tr(
"Advanced Options" ),
this );
55 QAction *mLoadFromLayerAction = mAdvancedMenu->addAction( tr(
"Load Classes from Layer" ) );
56 connect( mLoadFromLayerAction, &QAction::triggered,
this, &QgsPalettedRendererWidget::loadFromLayer );
57 QAction *loadFromFile = mAdvancedMenu->addAction( tr(
"Load Color Map from File…" ) );
58 connect( loadFromFile, &QAction::triggered,
this, &QgsPalettedRendererWidget::loadColorTable );
59 QAction *exportToFile = mAdvancedMenu->addAction( tr(
"Export Color Map to File…" ) );
60 connect( exportToFile, &QAction::triggered,
this, &QgsPalettedRendererWidget::saveColorTable );
63 mButtonAdvanced->setMenu( mAdvancedMenu );
65 mModel =
new QgsPalettedRendererModel(
this );
66 mProxyModel =
new QgsPalettedRendererProxyModel(
this );
67 mProxyModel->setSourceModel( mModel );
68 mTreeView->setSortingEnabled(
false );
69 mTreeView->setModel( mProxyModel );
73 mProxyModel->sort( QgsPalettedRendererModel::Column::ValueColumn );
76#ifdef ENABLE_MODELTEST
77 new ModelTest( mModel,
this );
80 mTreeView->setItemDelegateForColumn( QgsPalettedRendererModel::ColorColumn,
new QgsColorSwatchDelegate(
this ) );
82 mTreeView->setItemDelegateForColumn( QgsPalettedRendererModel::ValueColumn, mValueDelegate );
84 mTreeView->setColumnWidth( QgsPalettedRendererModel::ColorColumn,
Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance(
'X' ) * 6.6 );
85 mTreeView->setContextMenuPolicy( Qt::CustomContextMenu );
86 mTreeView->setSelectionMode( QAbstractItemView::ExtendedSelection );
87 mTreeView->setDragEnabled(
true );
88 mTreeView->setAcceptDrops(
true );
89 mTreeView->setDropIndicatorShown(
true );
90 mTreeView->setDragDropMode( QAbstractItemView::InternalMove );
91 mTreeView->setSelectionBehavior( QAbstractItemView::SelectRows );
92 mTreeView->setDefaultDropAction( Qt::MoveAction );
94 connect( mTreeView, &QTreeView::customContextMenuRequested,
this, [ = ]( QPoint ) { mContextMenu->exec( QCursor::pos() ); } );
96 btnColorRamp->setShowRandomColorRamp(
true );
114 connect( mDeleteEntryButton, &QPushButton::clicked,
this, &QgsPalettedRendererWidget::deleteEntry );
115 connect( mButtonDeleteAll, &QPushButton::clicked, mModel, &QgsPalettedRendererModel::deleteAll );
116 connect( mAddEntryButton, &QPushButton::clicked,
this, &QgsPalettedRendererWidget::addEntry );
117 connect( mClassifyButton, &QPushButton::clicked,
this, &QgsPalettedRendererWidget::classify );
125 mLoadFromLayerAction->setEnabled(
false );
144 int bandNumber = mBandComboBox->currentBand();
147 if ( !btnColorRamp->isNull() )
163 mModel->setClassData( pr->
classes() );
188void QgsPalettedRendererWidget::setSelectionColor(
const QItemSelection &selection,
const QColor &color )
193 QModelIndex colorIndex;
194 const auto constSelection = selection;
195 for (
const QItemSelectionRange &range : constSelection )
197 const auto constIndexes = range.indexes();
198 for (
const QModelIndex &index : constIndexes )
200 colorIndex = mModel->index( index.row(), QgsPalettedRendererModel::ColorColumn );
201 mModel->setData( colorIndex, color, Qt::EditRole );
209void QgsPalettedRendererWidget::deleteEntry()
214 QItemSelection sel = mProxyModel->mapSelectionToSource( mTreeView->selectionModel()->selection() );
215 const auto constSel = sel;
216 for (
const QItemSelectionRange &range : constSel )
218 if ( range.isValid() )
219 mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
227void QgsPalettedRendererWidget::addEntry()
231 QColor color( 150, 150, 150 );
232 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
235 color = ramp->color( 1.0 );
237 QModelIndex newEntry = mModel->addEntry( color );
238 mTreeView->scrollTo( newEntry );
239 mTreeView->selectionModel()->select( mProxyModel->mapFromSource( newEntry ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
244void QgsPalettedRendererWidget::changeColor()
246 QItemSelection sel = mProxyModel->mapSelectionToSource( mTreeView->selectionModel()->selection() );
247 QModelIndex colorIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::ColorColumn );
248 QColor currentColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
251 if ( panel && panel->dockMode() )
257 panel->openPanel( colorWidget );
263 if ( newColor.isValid() )
265 setSelectionColor( sel, newColor );
270void QgsPalettedRendererWidget::changeOpacity()
272 QItemSelection sel = mProxyModel->mapSelectionToSource( mTreeView->selectionModel()->selection() );
273 QModelIndex colorIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::ColorColumn );
274 QColor currentColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
277 double oldOpacity = ( currentColor.alpha() / 255.0 ) * 100.0;
278 double opacity = QInputDialog::getDouble(
this, tr(
"Opacity" ), tr(
"Change color opacity [%]" ), oldOpacity, 0.0, 100.0, 0, &ok );
281 int newOpacity = opacity / 100 * 255;
286 const auto constSel = sel;
287 for (
const QItemSelectionRange &range : constSel )
289 const auto constIndexes = range.indexes();
290 for (
const QModelIndex &index : constIndexes )
292 colorIndex = mModel->index( index.row(), QgsPalettedRendererModel::ColorColumn );
294 QColor newColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
295 newColor.setAlpha( newOpacity );
296 mModel->setData( colorIndex, newColor, Qt::EditRole );
305void QgsPalettedRendererWidget::changeLabel()
307 QItemSelection sel = mProxyModel->mapSelectionToSource( mTreeView->selectionModel()->selection() );
308 QModelIndex labelIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::LabelColumn );
309 QString currentLabel = mModel->data( labelIndex, Qt::DisplayRole ).toString();
312 QString newLabel = QInputDialog::getText(
this, tr(
"Label" ), tr(
"Change label" ), QLineEdit::Normal, currentLabel, &ok );
318 const auto constSel = sel;
319 for (
const QItemSelectionRange &range : constSel )
321 const auto constIndexes = range.indexes();
322 for (
const QModelIndex &index : constIndexes )
324 labelIndex = mModel->index( index.row(), QgsPalettedRendererModel::LabelColumn );
325 mModel->setData( labelIndex, newLabel, Qt::EditRole );
334void QgsPalettedRendererWidget::applyColorRamp()
336 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
345 QgsPalettedRasterRenderer::ClassData::iterator cIt = data.begin();
347 double numberOfEntries = data.count();
354 randomRamp->setTotalColorCount( numberOfEntries );
357 if ( numberOfEntries > 1 )
358 numberOfEntries -= 1;
360 for ( ; cIt != data.end(); ++cIt )
362 cIt->color = ramp->color( i / numberOfEntries );
365 mModel->setClassData( data );
371void QgsPalettedRendererWidget::loadColorTable()
374 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
375 QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Color Table from File" ), lastDir );
376 if ( !fileName.isEmpty() )
379 if ( !classes.isEmpty() )
382 mModel->setClassData( classes );
388 QMessageBox::critical(
nullptr, tr(
"Load Color Table" ), tr(
"Could not interpret file as a raster color table." ) );
393void QgsPalettedRendererWidget::saveColorTable()
396 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
397 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Color Table as File" ), lastDir, tr(
"Text (*.clr)" ) );
398 if ( !fileName.isEmpty() )
400 if ( !fileName.endsWith( QLatin1String(
".clr" ), Qt::CaseInsensitive ) )
402 fileName = fileName +
".clr";
405 QFile outputFile( fileName );
406 if ( outputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
408 QTextStream outputStream( &outputFile );
410 outputStream.flush();
413 QFileInfo fileInfo( fileName );
414 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
418 QMessageBox::warning(
this, tr(
"Save Color Table as File" ), tr(
"Write access denied. Adjust the file permissions and try again.\n\n" ) );
423void QgsPalettedRendererWidget::classify()
439 mGatherer =
new QgsPalettedRendererClassGatherer(
mRasterLayer, mBandComboBox->currentBand(), mModel->classData(), btnColorRamp->colorRamp() );
441 connect( mGatherer, &QgsPalettedRendererClassGatherer::progressChanged, mCalculatingProgressBar, [ = ](
int progress )
443 mCalculatingProgressBar->setValue( progress );
446 mCalculatingProgressBar->show();
447 mCancelButton->show();
448 connect( mCancelButton, &QPushButton::clicked, mGatherer, &QgsPalettedRendererClassGatherer::stop );
450 connect( mGatherer, &QgsPalettedRendererClassGatherer::collectedClasses,
this, &QgsPalettedRendererWidget::gatheredClasses );
451 connect( mGatherer, &QgsPalettedRendererClassGatherer::finished,
this, &QgsPalettedRendererWidget::gathererThreadFinished );
452 mClassifyButton->setText( tr(
"Calculating…" ) );
453 mClassifyButton->setEnabled(
false );
458void QgsPalettedRendererWidget::loadFromLayer()
464 QList<QgsColorRampShader::ColorRampItem> table = provider->
colorTable( mBandComboBox->currentBand() );
465 if ( !table.isEmpty() )
468 mModel->setClassData( classes );
474void QgsPalettedRendererWidget::bandChanged(
int band )
484 bool deleteExisting =
false;
485 if ( !mModel->classData().isEmpty() )
487 int res = QMessageBox::question(
this,
488 tr(
"Delete Classification" ),
489 tr(
"The classification band was changed from %1 to %2.\n"
490 "Should the existing classes be deleted?" ).arg( mBand ).arg( band ),
491 QMessageBox::Yes | QMessageBox::No );
493 deleteExisting = ( res == QMessageBox::Yes );
497 mModel->blockSignals(
true );
498 if ( deleteExisting )
501 mModel->blockSignals(
false );
505void QgsPalettedRendererWidget::gatheredClasses()
507 if ( !mGatherer || mGatherer->wasCanceled() )
510 mModel->setClassData( mGatherer->classes() );
514void QgsPalettedRendererWidget::gathererThreadFinished()
516 mGatherer->deleteLater();
518 mClassifyButton->setText( tr(
"Classify" ) );
519 mClassifyButton->setEnabled(
true );
520 mCalculatingProgressBar->hide();
521 mCancelButton->hide();
524void QgsPalettedRendererWidget::layerWillBeRemoved(
QgsMapLayer *layer )
538QgsPalettedRendererModel::QgsPalettedRendererModel( QObject *parent )
539 : QAbstractItemModel( parent )
551QModelIndex QgsPalettedRendererModel::index(
int row,
int column,
const QModelIndex &parent )
const
553 if ( column < 0 || column >= columnCount() )
556 return QModelIndex();
559 if ( !parent.isValid() && row >= 0 && row < mData.size() )
562 return createIndex( row, column );
566 return QModelIndex();
569QModelIndex QgsPalettedRendererModel::parent(
const QModelIndex &index )
const
574 return QModelIndex();
577int QgsPalettedRendererModel::columnCount(
const QModelIndex &parent )
const
579 if ( parent.isValid() )
585int QgsPalettedRendererModel::rowCount(
const QModelIndex &parent )
const
587 if ( parent.isValid() )
590 return mData.count();
593QVariant QgsPalettedRendererModel::data(
const QModelIndex &index,
int role )
const
595 if ( !index.isValid() )
600 case Qt::DisplayRole:
603 switch ( index.column() )
606 return mData.at( index.row() ).value;
609 return mData.at( index.row() ).color;
612 return mData.at( index.row() ).label;
623QVariant QgsPalettedRendererModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
625 switch ( orientation )
634 case Qt::DisplayRole:
639 return tr(
"Value" );
642 return tr(
"Color" );
645 return tr(
"Label" );
654 return QAbstractItemModel::headerData( section, orientation, role );
656 return QAbstractItemModel::headerData( section, orientation, role );
659bool QgsPalettedRendererModel::setData(
const QModelIndex &index,
const QVariant &value,
int )
661 if ( !index.isValid() )
663 if ( index.row() >= mData.length() )
666 switch ( index.column() )
671 double newValue = value.toDouble( &ok );
675 mData[ index.row() ].value = newValue;
676 emit dataChanged( index, index );
677 emit classesChanged();
683 mData[ index.row() ].color = value.value<QColor>();
684 emit dataChanged( index, index );
685 emit classesChanged();
691 mData[ index.row() ].label = value.toString();
692 emit dataChanged( index, index );
693 emit classesChanged();
701Qt::ItemFlags QgsPalettedRendererModel::flags(
const QModelIndex &index )
const
703 if ( !index.isValid() )
704 return QAbstractItemModel::flags( index ) | Qt::ItemIsDropEnabled;
706 Qt::ItemFlags f = QAbstractItemModel::flags( index );
707 switch ( index.column() )
712 f = f | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
715 return f | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
718bool QgsPalettedRendererModel::removeRows(
int row,
int count,
const QModelIndex &parent )
720 if ( row < 0 || row >= mData.count() )
722 if ( parent.isValid() )
725 for (
int i = row + count - 1; i >= row; --i )
727 beginRemoveRows( parent, i, i );
731 emit classesChanged();
735bool QgsPalettedRendererModel::insertRows(
int row,
int count,
const QModelIndex & )
737 QgsPalettedRasterRenderer::ClassData::const_iterator cIt = mData.constBegin();
738 int currentMaxValue = -std::numeric_limits<int>::max();
739 for ( ; cIt != mData.constEnd(); ++cIt )
741 int value = cIt->value;
742 currentMaxValue = std::max( value, currentMaxValue );
744 int nextValue = std::max( 0, currentMaxValue + 1 );
746 beginInsertRows( QModelIndex(), row, row + count - 1 );
747 for (
int i = row; i < row + count; ++i, ++nextValue )
752 emit classesChanged();
756Qt::DropActions QgsPalettedRendererModel::supportedDropActions()
const
758 return Qt::MoveAction;
761QStringList QgsPalettedRendererModel::mimeTypes()
const
764 types << QStringLiteral(
"application/x-qgspalettedrenderermodel" );
768QMimeData *QgsPalettedRendererModel::mimeData(
const QModelIndexList &indexes )
const
770 QMimeData *mimeData =
new QMimeData();
771 QByteArray encodedData;
773 QDataStream stream( &encodedData, QIODevice::WriteOnly );
776 const auto constIndexes = indexes;
777 for (
const QModelIndex &index : constIndexes )
779 if ( !index.isValid() || index.column() != 0 )
782 stream << index.row();
784 mimeData->setData( QStringLiteral(
"application/x-qgspalettedrenderermodel" ), encodedData );
788bool QgsPalettedRendererModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex & )
791 if ( action != Qt::MoveAction )
return true;
793 if ( !data->hasFormat( QStringLiteral(
"application/x-qgspalettedrenderermodel" ) ) )
796 QByteArray encodedData = data->data( QStringLiteral(
"application/x-qgspalettedrenderermodel" ) );
797 QDataStream stream( &encodedData, QIODevice::ReadOnly );
800 while ( !stream.atEnd() )
808 for (
int i = 0; i < rows.count(); ++i )
809 newData << mData.at( rows.at( i ) );
814 beginInsertRows( QModelIndex(), row, row + rows.count() - 1 );
815 for (
int i = 0; i < rows.count(); ++i )
816 mData.insert( row + i, newData.at( i ) );
818 emit classesChanged();
822QModelIndex QgsPalettedRendererModel::addEntry(
const QColor &color )
824 insertRow( rowCount() );
825 QModelIndex newRow = index( mData.count() - 1, 1 );
826 setData( newRow, color );
830void QgsPalettedRendererModel::deleteAll()
835 emit classesChanged();
844 , mBandNumber( bandNumber )
846 , mClasses( existingClasses )
847 , mWasCanceled( false )
850void QgsPalettedRendererClassGatherer::run()
852 mWasCanceled =
false;
861 QgsPalettedRasterRenderer::ClassData::iterator classIt = newClasses.begin();
862 emit progressChanged( 0 );
864 for ( ; classIt != newClasses.end(); ++classIt )
869 if ( existingClass.value == classIt->value )
871 classIt->color = existingClass.color;
872 classIt->label = existingClass.label;
877 emit progressChanged( 100 * ( i /
static_cast<float>( newClasses.count() ) ) );
879 mClasses = newClasses;
882 mFeedbackMutex.lock();
885 mFeedbackMutex.unlock();
887 emit collectedClasses();
894 for (
int i = 0; i < rowCount( ); ++i )
896 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.