26 #include <QColorDialog> 27 #include <QInputDialog> 28 #include <QFileDialog> 29 #include <QMessageBox> 32 #ifdef ENABLE_MODELTEST 33 #include "modeltest.h" 40 mCalculatingProgressBar->hide();
41 mCancelButton->hide();
43 mContextMenu =
new QMenu( tr(
"Options" ),
this );
44 mContextMenu->addAction( tr(
"Change Color…" ),
this, SLOT( changeColor() ) );
45 mContextMenu->addAction( tr(
"Change Opacity…" ),
this, SLOT( changeOpacity() ) );
46 mContextMenu->addAction( tr(
"Change Label…" ),
this, SLOT( changeLabel() ) );
48 mAdvancedMenu =
new QMenu( tr(
"Advanced Options" ),
this );
49 QAction *mLoadFromLayerAction = mAdvancedMenu->addAction( tr(
"Load Classes from Layer" ) );
50 connect( mLoadFromLayerAction, &QAction::triggered,
this, &QgsPalettedRendererWidget::loadFromLayer );
51 QAction *loadFromFile = mAdvancedMenu->addAction( tr(
"Load Color Map from File…" ) );
52 connect( loadFromFile, &QAction::triggered,
this, &QgsPalettedRendererWidget::loadColorTable );
53 QAction *exportToFile = mAdvancedMenu->addAction( tr(
"Export Color Map to File…" ) );
54 connect( exportToFile, &QAction::triggered,
this, &QgsPalettedRendererWidget::saveColorTable );
57 mButtonAdvanced->setMenu( mAdvancedMenu );
59 mModel =
new QgsPalettedRendererModel(
this );
60 mTreeView->setSortingEnabled(
false );
61 mTreeView->setModel( mModel );
63 #ifdef ENABLE_MODELTEST 64 new ModelTest( mModel,
this );
68 mTreeView->setItemDelegateForColumn( QgsPalettedRendererModel::ColorColumn, mSwatchDelegate );
69 mTreeView->setColumnWidth( QgsPalettedRendererModel::ColorColumn,
Qgis::UI_SCALE_FACTOR * fontMetrics().width(
'X' ) * 6.6 );
70 mTreeView->setContextMenuPolicy( Qt::CustomContextMenu );
71 mTreeView->setSelectionMode( QAbstractItemView::ExtendedSelection );
72 mTreeView->setDragEnabled(
true );
73 mTreeView->setAcceptDrops(
true );
74 mTreeView->setDropIndicatorShown(
true );
75 mTreeView->setDragDropMode( QAbstractItemView::InternalMove );
76 mTreeView->setSelectionBehavior( QAbstractItemView::SelectRows );
77 mTreeView->setDefaultDropAction( Qt::MoveAction );
79 connect( mTreeView, &QTreeView::customContextMenuRequested,
this, [ = ]( QPoint ) { mContextMenu->exec( QCursor::pos() ); }
82 btnColorRamp->setShowRandomColorRamp(
true );
100 connect( mDeleteEntryButton, &QPushButton::clicked,
this, &QgsPalettedRendererWidget::deleteEntry );
101 connect( mButtonDeleteAll, &QPushButton::clicked, mModel, &QgsPalettedRendererModel::deleteAll );
102 connect( mAddEntryButton, &QPushButton::clicked,
this, &QgsPalettedRendererWidget::addEntry );
103 connect( mClassifyButton, &QPushButton::clicked,
this, &QgsPalettedRendererWidget::classify );
108 mLoadFromLayerAction->setEnabled( !provider->
colorTable( mBandComboBox->currentBand() ).isEmpty() );
112 mLoadFromLayerAction->setEnabled(
false );
131 int bandNumber = mBandComboBox->currentBand();
134 if ( !btnColorRamp->isNull() )
150 mModel->setClassData( pr->
classes() );
170 void QgsPalettedRendererWidget::setSelectionColor(
const QItemSelection &selection,
const QColor &color )
175 QModelIndex colorIndex;
176 Q_FOREACH (
const QItemSelectionRange &range, selection )
178 Q_FOREACH (
const QModelIndex &index, range.indexes() )
180 colorIndex = mModel->index( index.row(), QgsPalettedRendererModel::ColorColumn );
181 mModel->setData( colorIndex, color, Qt::EditRole );
189 void QgsPalettedRendererWidget::deleteEntry()
194 QItemSelection sel = mTreeView->selectionModel()->selection();
195 Q_FOREACH (
const QItemSelectionRange &range, sel )
197 if ( range.isValid() )
198 mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
206 void QgsPalettedRendererWidget::addEntry()
210 QColor color( 150, 150, 150 );
211 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
214 color = ramp->color( 1.0 );
216 QModelIndex newEntry = mModel->addEntry( color );
217 mTreeView->scrollTo( newEntry );
218 mTreeView->selectionModel()->select( newEntry, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
223 void QgsPalettedRendererWidget::changeColor()
225 QItemSelection sel = mTreeView->selectionModel()->selection();
227 QModelIndex colorIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::ColorColumn );
228 QColor currentColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
231 if ( panel && panel->dockMode() )
237 panel->openPanel( colorWidget );
243 if ( newColor.isValid() )
245 setSelectionColor( sel, newColor );
250 void QgsPalettedRendererWidget::changeOpacity()
252 QItemSelection sel = mTreeView->selectionModel()->selection();
254 QModelIndex colorIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::ColorColumn );
255 QColor currentColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
258 double oldOpacity = ( currentColor.alpha() / 255.0 ) * 100.0;
259 double opacity = QInputDialog::getDouble(
this, tr(
"Opacity" ), tr(
"Change color opacity [%]" ), oldOpacity, 0.0, 100.0, 0, &ok );
262 int newOpacity = opacity / 100 * 255;
267 Q_FOREACH (
const QItemSelectionRange &range, sel )
269 Q_FOREACH (
const QModelIndex &index, range.indexes() )
271 colorIndex = mModel->index( index.row(), QgsPalettedRendererModel::ColorColumn );
273 QColor newColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
274 newColor.setAlpha( newOpacity );
275 mModel->setData( colorIndex, newColor, Qt::EditRole );
284 void QgsPalettedRendererWidget::changeLabel()
286 QItemSelection sel = mTreeView->selectionModel()->selection();
288 QModelIndex labelIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::LabelColumn );
289 QString currentLabel = mModel->data( labelIndex, Qt::DisplayRole ).toString();
292 QString newLabel = QInputDialog::getText(
this, tr(
"Label" ), tr(
"Change label" ), QLineEdit::Normal, currentLabel, &ok );
298 Q_FOREACH (
const QItemSelectionRange &range, sel )
300 Q_FOREACH (
const QModelIndex &index, range.indexes() )
302 labelIndex = mModel->index( index.row(), QgsPalettedRendererModel::LabelColumn );
303 mModel->setData( labelIndex, newLabel, Qt::EditRole );
312 void QgsPalettedRendererWidget::applyColorRamp()
314 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
323 QgsPalettedRasterRenderer::ClassData::iterator cIt = data.begin();
325 double numberOfEntries = data.count();
328 if (
QgsRandomColorRamp *randomRamp = dynamic_cast<QgsRandomColorRamp *>( ramp.get() ) )
332 randomRamp->setTotalColorCount( numberOfEntries );
335 if ( numberOfEntries > 1 )
336 numberOfEntries -= 1;
338 for ( ; cIt != data.end(); ++cIt )
340 cIt->color = ramp->color( i / numberOfEntries );
343 mModel->setClassData( data );
349 void QgsPalettedRendererWidget::loadColorTable()
352 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
353 QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Color Table from File" ), lastDir );
354 if ( !fileName.isEmpty() )
357 if ( !classes.isEmpty() )
360 mModel->setClassData( classes );
366 QMessageBox::critical(
nullptr, tr(
"Load Color Table" ), tr(
"Could not interpret file as a raster color table." ) );
371 void QgsPalettedRendererWidget::saveColorTable()
374 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
375 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Color Table as File" ), lastDir, tr(
"Text (*.clr)" ) );
376 if ( !fileName.isEmpty() )
378 if ( !fileName.endsWith( QLatin1String(
".clr" ), Qt::CaseInsensitive ) )
380 fileName = fileName +
".clr";
383 QFile outputFile( fileName );
384 if ( outputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
386 QTextStream outputStream( &outputFile );
388 outputStream.flush();
391 QFileInfo fileInfo( fileName );
392 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
396 QMessageBox::warning(
this, tr(
"Save Color Table as File" ), tr(
"Write access denied. Adjust the file permissions and try again.\n\n" ) );
401 void QgsPalettedRendererWidget::classify()
417 mGatherer =
new QgsPalettedRendererClassGatherer(
mRasterLayer, mBandComboBox->currentBand(), mModel->classData(), btnColorRamp->colorRamp() );
419 connect( mGatherer, &QgsPalettedRendererClassGatherer::progressChanged, mCalculatingProgressBar, &QProgressBar::setValue );
420 mCalculatingProgressBar->show();
421 mCancelButton->show();
422 connect( mCancelButton, &QPushButton::clicked, mGatherer, &QgsPalettedRendererClassGatherer::stop );
424 connect( mGatherer, &QgsPalettedRendererClassGatherer::collectedClasses,
this, &QgsPalettedRendererWidget::gatheredClasses );
425 connect( mGatherer, &QgsPalettedRendererClassGatherer::finished,
this, &QgsPalettedRendererWidget::gathererThreadFinished );
426 mClassifyButton->setText( tr(
"Calculating…" ) );
427 mClassifyButton->setEnabled(
false );
432 void QgsPalettedRendererWidget::loadFromLayer()
438 QList<QgsColorRampShader::ColorRampItem> table = provider->
colorTable( mBandComboBox->currentBand() );
439 if ( !table.isEmpty() )
442 mModel->setClassData( classes );
448 void QgsPalettedRendererWidget::bandChanged(
int band )
453 bool deleteExisting =
false;
454 if ( !mModel->classData().isEmpty() )
456 int res = QMessageBox::question(
this,
457 tr(
"Delete Classification" ),
458 tr(
"The classification band was changed from %1 to %2.\n" 459 "Should the existing classes be deleted?" ).arg( mBand ).arg( band ),
460 QMessageBox::Yes | QMessageBox::No );
462 deleteExisting = ( res == QMessageBox::Yes );
466 mModel->blockSignals(
true );
467 if ( deleteExisting )
470 mModel->blockSignals(
false );
474 void QgsPalettedRendererWidget::gatheredClasses()
476 if ( !mGatherer || mGatherer->wasCanceled() )
479 mModel->setClassData( mGatherer->classes() );
483 void QgsPalettedRendererWidget::gathererThreadFinished()
485 mGatherer->deleteLater();
487 mClassifyButton->setText( tr(
"Classify" ) );
488 mClassifyButton->setEnabled(
true );
489 mCalculatingProgressBar->hide();
490 mCancelButton->hide();
493 void QgsPalettedRendererWidget::layerWillBeRemoved(
QgsMapLayer *layer )
507 QgsPalettedRendererModel::QgsPalettedRendererModel( QObject *parent )
508 : QAbstractItemModel( parent )
520 QModelIndex QgsPalettedRendererModel::index(
int row,
int column,
const QModelIndex &parent )
const 522 if ( column < 0 || column >= columnCount() )
525 return QModelIndex();
528 if ( !parent.isValid() && row >= 0 && row < mData.size() )
531 return createIndex( row, column );
535 return QModelIndex();
538 QModelIndex QgsPalettedRendererModel::parent(
const QModelIndex &index )
const 543 return QModelIndex();
546 int QgsPalettedRendererModel::columnCount(
const QModelIndex &parent )
const 548 if ( parent.isValid() )
554 int QgsPalettedRendererModel::rowCount(
const QModelIndex &parent )
const 556 if ( parent.isValid() )
559 return mData.count();
562 QVariant QgsPalettedRendererModel::data(
const QModelIndex &index,
int role )
const 564 if ( !index.isValid() )
569 case Qt::DisplayRole:
572 switch ( index.column() )
575 return mData.at( index.row() ).value;
578 return mData.at( index.row() ).color;
581 return mData.at( index.row() ).label;
592 QVariant QgsPalettedRendererModel::headerData(
int section, Qt::Orientation orientation,
int role )
const 594 switch ( orientation )
603 case Qt::DisplayRole:
608 return tr(
"Value" );
611 return tr(
"Color" );
614 return tr(
"Label" );
623 return QAbstractItemModel::headerData( section, orientation, role );
625 return QAbstractItemModel::headerData( section, orientation, role );
628 bool QgsPalettedRendererModel::setData(
const QModelIndex &index,
const QVariant &value,
int )
630 if ( !index.isValid() )
632 if ( index.row() >= mData.length() )
635 switch ( index.column() )
640 int newValue = value.toInt( &ok );
644 mData[ index.row() ].value = newValue;
645 emit dataChanged( index, index );
646 emit classesChanged();
652 mData[ index.row() ].color = value.value<QColor>();
653 emit dataChanged( index, index );
654 emit classesChanged();
660 mData[ index.row() ].label = value.toString();
661 emit dataChanged( index, index );
662 emit classesChanged();
670 Qt::ItemFlags QgsPalettedRendererModel::flags(
const QModelIndex &index )
const 672 if ( !index.isValid() )
673 return QAbstractItemModel::flags( index ) | Qt::ItemIsDropEnabled;
675 Qt::ItemFlags f = QAbstractItemModel::flags( index );
676 switch ( index.column() )
681 f = f | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
684 return f | Qt::ItemIsEnabled | Qt::ItemIsSelectable;;
687 bool QgsPalettedRendererModel::removeRows(
int row,
int count,
const QModelIndex &parent )
689 if ( row < 0 || row >= mData.count() )
691 if ( parent.isValid() )
694 for (
int i = row + count - 1; i >= row; --i )
696 beginRemoveRows( parent, i, i );
700 emit classesChanged();
704 bool QgsPalettedRendererModel::insertRows(
int row,
int count,
const QModelIndex & )
706 QgsPalettedRasterRenderer::ClassData::const_iterator cIt = mData.constBegin();
707 int currentMaxValue = -std::numeric_limits<int>::max();
708 for ( ; cIt != mData.constEnd(); ++cIt )
710 int value = cIt->value;
711 currentMaxValue = std::max( value, currentMaxValue );
713 int nextValue = std::max( 0, currentMaxValue + 1 );
715 beginInsertRows( QModelIndex(), row, row + count - 1 );
716 for (
int i = row; i < row + count; ++i, ++nextValue )
721 emit classesChanged();
725 Qt::DropActions QgsPalettedRendererModel::supportedDropActions()
const 727 return Qt::MoveAction;
730 QStringList QgsPalettedRendererModel::mimeTypes()
const 733 types << QStringLiteral(
"application/x-qgspalettedrenderermodel" );
737 QMimeData *QgsPalettedRendererModel::mimeData(
const QModelIndexList &indexes )
const 739 QMimeData *mimeData =
new QMimeData();
740 QByteArray encodedData;
742 QDataStream stream( &encodedData, QIODevice::WriteOnly );
745 Q_FOREACH (
const QModelIndex &index, indexes )
747 if ( !index.isValid() || index.column() != 0 )
750 stream << index.row();
752 mimeData->setData( QStringLiteral(
"application/x-qgspalettedrenderermodel" ), encodedData );
756 bool QgsPalettedRendererModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex & )
759 if ( action != Qt::MoveAction )
return true;
761 if ( !data->hasFormat( QStringLiteral(
"application/x-qgspalettedrenderermodel" ) ) )
764 QByteArray encodedData = data->data( QStringLiteral(
"application/x-qgspalettedrenderermodel" ) );
765 QDataStream stream( &encodedData, QIODevice::ReadOnly );
768 while ( !stream.atEnd() )
776 for (
int i = 0; i < rows.count(); ++i )
777 newData << mData.at( rows.at( i ) );
782 beginInsertRows( QModelIndex(), row, row + rows.count() - 1 );
783 for (
int i = 0; i < rows.count(); ++i )
784 mData.insert( row + i, newData.at( i ) );
786 emit classesChanged();
790 QModelIndex QgsPalettedRendererModel::addEntry(
const QColor &color )
792 insertRow( rowCount() );
793 QModelIndex newRow = index( mData.count() - 1, 1 );
794 setData( newRow, color );
798 void QgsPalettedRendererModel::deleteAll()
803 emit classesChanged();
806 void QgsPalettedRendererClassGatherer::run()
808 mWasCanceled =
false;
817 QgsPalettedRasterRenderer::ClassData::iterator classIt = newClasses.begin();
818 for ( ; classIt != newClasses.end(); ++classIt )
823 if ( existingClass.value == classIt->value )
825 classIt->color = existingClass.color;
826 classIt->label = existingClass.label;
831 mClasses = newClasses;
834 mFeedbackMutex.lock();
837 mFeedbackMutex.unlock();
839 emit collectedClasses();
void setSourceColorRamp(QgsColorRamp *ramp)
Set the source color ramp.
static QString classDataToString(const QgsPalettedRasterRenderer::ClassData &classes)
Converts classes to a string representation, using the .clr/gdal color table file format...
static QColor getColor(const QColor &initialColor, QWidget *parent, const QString &title=QString(), bool allowOpacity=false)
Returns a color selection from a color dialog.
A rectangle specified with double values.
Base class for all map layer types.
Renderer for paletted raster images.
static const double UI_SCALE_FACTOR
UI scaling factor.
This class is a composition of two QSettings instances:
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
Properties of a single value class.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
QgsRasterDataProvider * dataProvider() override
Returns the layer's data provider.
int band() const
Returns the raster band used for rendering the raster.
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
QgsRasterRenderer * renderer() const
ClassData classes() const
Returns a map of value to classes (colors) used by the renderer.
QList< QgsPalettedRasterRenderer::Class > ClassData
Map of value to class properties.
void bandChanged(int band)
This signal is emitted when the currently selected band changes.
Reads and writes project states.
Totally random color ramp.
void layerWillBeRemoved(const QString &layerId)
Emitted when a layer is about to be removed from the registry.
static QgsPalettedRasterRenderer::ClassData classDataFromFile(const QString &path)
Opens a color table file and returns corresponding paletted renderer class data.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
static QgsPalettedRasterRenderer::ClassData classDataFromRaster(QgsRasterInterface *raster, int bandNumber, QgsColorRamp *ramp=nullptr, QgsRasterBlockFeedback *feedback=nullptr)
Generates class data from a raster, for the specified bandNumber.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsColorRamp * sourceColorRamp() const
Gets the source color ramp.
virtual QList< QgsColorRampShader::ColorRampItem > colorTable(int bandNo) const
Feedback object tailored for raster block reading.
static QgsPalettedRasterRenderer::ClassData colorTableToClassData(const QList< QgsColorRampShader::ColorRampItem > &table)
Converts a raster color table to paletted renderer class data.
Raster renderer pipe that applies colors to a raster.
A delegate for showing a color swatch in a list.
Base class for raster data providers.