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, 50 );
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.
This class is a composition of two QSettings instances:
virtual QList< QgsColorRampShader::ColorRampItem > colorTable(int bandNo) const
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
ClassData classes() const
Returns a map of value to classes (colors) used by the renderer.
Properties of a single value class.
QgsRasterRenderer * renderer() const
QgsRasterDataProvider * dataProvider() override
Returns the layer's data provider.
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
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.
QgsColorRamp * sourceColorRamp() const
Gets the source 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.
int band() const
Returns the raster band used for rendering the raster.
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.