19#include "moc_qgspalettedrendererwidget.cpp"
29#include <QColorDialog>
30#include <QInputDialog>
37#ifdef ENABLE_MODELTEST
47 mCalculatingProgressBar->hide();
48 mCancelButton->hide();
50 mContextMenu =
new QMenu( tr(
"Options" ),
this );
51 mContextMenu->addAction( tr(
"Change Color…" ),
this, SLOT( changeColor() ) );
52 mContextMenu->addAction( tr(
"Change Opacity…" ),
this, SLOT( changeOpacity() ) );
53 mContextMenu->addAction( tr(
"Change Label…" ),
this, SLOT( changeLabel() ) );
55 mAdvancedMenu =
new QMenu( tr(
"Advanced Options" ),
this );
56 QAction *mLoadFromLayerAction = mAdvancedMenu->addAction( tr(
"Load Classes from Layer" ) );
57 connect( mLoadFromLayerAction, &QAction::triggered,
this, &QgsPalettedRendererWidget::loadFromLayer );
58 QAction *loadFromFile = mAdvancedMenu->addAction( tr(
"Load Color Map from File…" ) );
59 connect( loadFromFile, &QAction::triggered,
this, &QgsPalettedRendererWidget::loadColorTable );
60 QAction *exportToFile = mAdvancedMenu->addAction( tr(
"Export Color Map to File…" ) );
61 connect( exportToFile, &QAction::triggered,
this, &QgsPalettedRendererWidget::saveColorTable );
64 mButtonAdvanced->setMenu( mAdvancedMenu );
66 mModel =
new QgsPalettedRendererModel(
this );
67 mProxyModel =
new QgsPalettedRendererProxyModel(
this );
68 mProxyModel->setSourceModel( mModel );
69 mTreeView->setSortingEnabled(
false );
70 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 ) {
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, tr(
"Delete Classification" ), tr(
"The classification band was changed from %1 to %2.\n"
487 "Should the existing classes be deleted?" )
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 )
549QModelIndex QgsPalettedRendererModel::index(
int row,
int column,
const QModelIndex &parent )
const
551 if ( column < 0 || column >= columnCount() )
554 return QModelIndex();
557 if ( !parent.isValid() && row >= 0 && row < mData.size() )
560 return createIndex( row, column );
564 return QModelIndex();
567QModelIndex QgsPalettedRendererModel::parent(
const QModelIndex &index )
const
572 return QModelIndex();
575int QgsPalettedRendererModel::columnCount(
const QModelIndex &parent )
const
577 if ( parent.isValid() )
583int QgsPalettedRendererModel::rowCount(
const QModelIndex &parent )
const
585 if ( parent.isValid() )
588 return mData.count();
591QVariant QgsPalettedRendererModel::data(
const QModelIndex &index,
int role )
const
593 if ( !index.isValid() )
598 case Qt::DisplayRole:
601 switch ( index.column() )
604 return mData.at( index.row() ).value;
607 return mData.at( index.row() ).color;
610 return mData.at( index.row() ).label;
621QVariant QgsPalettedRendererModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
623 switch ( orientation )
632 case Qt::DisplayRole:
637 return tr(
"Value" );
640 return tr(
"Color" );
643 return tr(
"Label" );
651 return QAbstractItemModel::headerData( section, orientation, role );
653 return QAbstractItemModel::headerData( section, orientation, role );
656bool QgsPalettedRendererModel::setData(
const QModelIndex &index,
const QVariant &value,
int )
658 if ( !index.isValid() )
660 if ( index.row() >= mData.length() )
663 switch ( index.column() )
668 double newValue = value.toDouble( &ok );
672 mData[index.row()].value = newValue;
673 emit dataChanged( index, index );
674 emit classesChanged();
680 mData[index.row()].color = value.value<QColor>();
681 emit dataChanged( index, index );
682 emit classesChanged();
688 mData[index.row()].label = value.toString();
689 emit dataChanged( index, index );
690 emit classesChanged();
698Qt::ItemFlags QgsPalettedRendererModel::flags(
const QModelIndex &index )
const
700 if ( !index.isValid() )
701 return QAbstractItemModel::flags( index ) | Qt::ItemIsDropEnabled;
703 Qt::ItemFlags f = QAbstractItemModel::flags( index );
704 switch ( index.column() )
709 f = f | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
712 return f | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
715bool QgsPalettedRendererModel::removeRows(
int row,
int count,
const QModelIndex &parent )
717 if ( row < 0 || row >= mData.count() )
719 if ( parent.isValid() )
722 for (
int i = row + count - 1; i >= row; --i )
724 beginRemoveRows( parent, i, i );
728 emit classesChanged();
732bool QgsPalettedRendererModel::insertRows(
int row,
int count,
const QModelIndex & )
734 QgsPalettedRasterRenderer::ClassData::const_iterator cIt = mData.constBegin();
735 int currentMaxValue = -std::numeric_limits<int>::max();
736 for ( ; cIt != mData.constEnd(); ++cIt )
738 int value = cIt->value;
739 currentMaxValue = std::max( value, currentMaxValue );
741 int nextValue = std::max( 0, currentMaxValue + 1 );
743 beginInsertRows( QModelIndex(), row, row + count - 1 );
744 for (
int i = row; i < row + count; ++i, ++nextValue )
749 emit classesChanged();
753Qt::DropActions QgsPalettedRendererModel::supportedDropActions()
const
755 return Qt::MoveAction;
758QStringList QgsPalettedRendererModel::mimeTypes()
const
761 types << QStringLiteral(
"application/x-qgspalettedrenderermodel" );
765QMimeData *QgsPalettedRendererModel::mimeData(
const QModelIndexList &indexes )
const
767 QMimeData *mimeData =
new QMimeData();
768 QByteArray encodedData;
770 QDataStream stream( &encodedData, QIODevice::WriteOnly );
773 const auto constIndexes = indexes;
774 for (
const QModelIndex &index : constIndexes )
776 if ( !index.isValid() || index.column() != 0 )
779 stream << index.row();
781 mimeData->setData( QStringLiteral(
"application/x-qgspalettedrenderermodel" ), encodedData );
785bool QgsPalettedRendererModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex & )
788 if ( action != Qt::MoveAction )
791 if ( !data->hasFormat( QStringLiteral(
"application/x-qgspalettedrenderermodel" ) ) )
794 QByteArray encodedData = data->data( QStringLiteral(
"application/x-qgspalettedrenderermodel" ) );
795 QDataStream stream( &encodedData, QIODevice::ReadOnly );
798 while ( !stream.atEnd() )
806 for (
int i = 0; i < rows.count(); ++i )
807 newData << mData.at( rows.at( i ) );
812 beginInsertRows( QModelIndex(), row, row + rows.count() - 1 );
813 for (
int i = 0; i < rows.count(); ++i )
814 mData.insert( row + i, newData.at( i ) );
816 emit classesChanged();
820QModelIndex QgsPalettedRendererModel::addEntry(
const QColor &color )
822 insertRow( rowCount() );
823 QModelIndex newRow = index( mData.count() - 1, 1 );
824 setData( newRow, color );
828void QgsPalettedRendererModel::deleteAll()
833 emit classesChanged();
841 : mProvider( ( layer && layer->dataProvider() ) ? layer->dataProvider()->clone() : nullptr )
842 , mBandNumber( bandNumber )
844 , mClasses( existingClasses )
845 , mWasCanceled( false )
848void QgsPalettedRendererClassGatherer::run()
850 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 * (
static_cast<double>( i ) /
static_cast<double>( newClasses.count() ) ) );
879 mClasses = newClasses;
883 mFeedbackMutex.lock();
886 mFeedbackMutex.unlock();
888 emit collectedClasses();
895 for (
int i = 0; i < rowCount(); ++i )
897 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.
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.
int inputBand() const override
Returns the input band for the renderer, or -1 if no input band is available.
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.