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 const auto constSelection = selection;
177 for (
const QItemSelectionRange &range : constSelection )
179 const auto constIndexes = range.indexes();
180 for (
const QModelIndex &index : constIndexes )
182 colorIndex = mModel->index( index.row(), QgsPalettedRendererModel::ColorColumn );
183 mModel->setData( colorIndex, color, Qt::EditRole );
191 void QgsPalettedRendererWidget::deleteEntry()
196 QItemSelection sel = mTreeView->selectionModel()->selection();
197 const auto constSel = sel;
198 for (
const QItemSelectionRange &range : constSel )
200 if ( range.isValid() )
201 mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
209 void QgsPalettedRendererWidget::addEntry()
213 QColor color( 150, 150, 150 );
214 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
217 color = ramp->color( 1.0 );
219 QModelIndex newEntry = mModel->addEntry( color );
220 mTreeView->scrollTo( newEntry );
221 mTreeView->selectionModel()->select( newEntry, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
226 void QgsPalettedRendererWidget::changeColor()
228 QItemSelection sel = mTreeView->selectionModel()->selection();
230 QModelIndex colorIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::ColorColumn );
231 QColor currentColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
234 if ( panel && panel->dockMode() )
240 panel->openPanel( colorWidget );
246 if ( newColor.isValid() )
248 setSelectionColor( sel, newColor );
253 void QgsPalettedRendererWidget::changeOpacity()
255 QItemSelection sel = mTreeView->selectionModel()->selection();
257 QModelIndex colorIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::ColorColumn );
258 QColor currentColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
261 double oldOpacity = ( currentColor.alpha() / 255.0 ) * 100.0;
262 double opacity = QInputDialog::getDouble(
this, tr(
"Opacity" ), tr(
"Change color opacity [%]" ), oldOpacity, 0.0, 100.0, 0, &ok );
265 int newOpacity = opacity / 100 * 255;
270 const auto constSel = sel;
271 for (
const QItemSelectionRange &range : constSel )
273 const auto constIndexes = range.indexes();
274 for (
const QModelIndex &index : constIndexes )
276 colorIndex = mModel->index( index.row(), QgsPalettedRendererModel::ColorColumn );
278 QColor newColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
279 newColor.setAlpha( newOpacity );
280 mModel->setData( colorIndex, newColor, Qt::EditRole );
289 void QgsPalettedRendererWidget::changeLabel()
291 QItemSelection sel = mTreeView->selectionModel()->selection();
293 QModelIndex labelIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::LabelColumn );
294 QString currentLabel = mModel->data( labelIndex, Qt::DisplayRole ).toString();
297 QString newLabel = QInputDialog::getText(
this, tr(
"Label" ), tr(
"Change label" ), QLineEdit::Normal, currentLabel, &ok );
303 const auto constSel = sel;
304 for (
const QItemSelectionRange &range : constSel )
306 const auto constIndexes = range.indexes();
307 for (
const QModelIndex &index : constIndexes )
309 labelIndex = mModel->index( index.row(), QgsPalettedRendererModel::LabelColumn );
310 mModel->setData( labelIndex, newLabel, Qt::EditRole );
319 void QgsPalettedRendererWidget::applyColorRamp()
321 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
330 QgsPalettedRasterRenderer::ClassData::iterator cIt = data.begin();
332 double numberOfEntries = data.count();
335 if (
QgsRandomColorRamp *randomRamp = dynamic_cast<QgsRandomColorRamp *>( ramp.get() ) )
339 randomRamp->setTotalColorCount( numberOfEntries );
342 if ( numberOfEntries > 1 )
343 numberOfEntries -= 1;
345 for ( ; cIt != data.end(); ++cIt )
347 cIt->color = ramp->color( i / numberOfEntries );
350 mModel->setClassData( data );
356 void QgsPalettedRendererWidget::loadColorTable()
359 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
360 QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Color Table from File" ), lastDir );
361 if ( !fileName.isEmpty() )
364 if ( !classes.isEmpty() )
367 mModel->setClassData( classes );
373 QMessageBox::critical(
nullptr, tr(
"Load Color Table" ), tr(
"Could not interpret file as a raster color table." ) );
378 void QgsPalettedRendererWidget::saveColorTable()
381 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
382 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Color Table as File" ), lastDir, tr(
"Text (*.clr)" ) );
383 if ( !fileName.isEmpty() )
385 if ( !fileName.endsWith( QLatin1String(
".clr" ), Qt::CaseInsensitive ) )
387 fileName = fileName +
".clr";
390 QFile outputFile( fileName );
391 if ( outputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
393 QTextStream outputStream( &outputFile );
395 outputStream.flush();
398 QFileInfo fileInfo( fileName );
399 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
403 QMessageBox::warning(
this, tr(
"Save Color Table as File" ), tr(
"Write access denied. Adjust the file permissions and try again.\n\n" ) );
408 void QgsPalettedRendererWidget::classify()
424 mGatherer =
new QgsPalettedRendererClassGatherer(
mRasterLayer, mBandComboBox->currentBand(), mModel->classData(), btnColorRamp->colorRamp() );
426 connect( mGatherer, &QgsPalettedRendererClassGatherer::progressChanged, mCalculatingProgressBar, &QProgressBar::setValue );
427 mCalculatingProgressBar->show();
428 mCancelButton->show();
429 connect( mCancelButton, &QPushButton::clicked, mGatherer, &QgsPalettedRendererClassGatherer::stop );
431 connect( mGatherer, &QgsPalettedRendererClassGatherer::collectedClasses,
this, &QgsPalettedRendererWidget::gatheredClasses );
432 connect( mGatherer, &QgsPalettedRendererClassGatherer::finished,
this, &QgsPalettedRendererWidget::gathererThreadFinished );
433 mClassifyButton->setText( tr(
"Calculating…" ) );
434 mClassifyButton->setEnabled(
false );
439 void QgsPalettedRendererWidget::loadFromLayer()
445 QList<QgsColorRampShader::ColorRampItem> table = provider->
colorTable( mBandComboBox->currentBand() );
446 if ( !table.isEmpty() )
449 mModel->setClassData( classes );
455 void QgsPalettedRendererWidget::bandChanged(
int band )
460 bool deleteExisting =
false;
461 if ( !mModel->classData().isEmpty() )
463 int res = QMessageBox::question(
this,
464 tr(
"Delete Classification" ),
465 tr(
"The classification band was changed from %1 to %2.\n" 466 "Should the existing classes be deleted?" ).arg( mBand ).arg( band ),
467 QMessageBox::Yes | QMessageBox::No );
469 deleteExisting = ( res == QMessageBox::Yes );
473 mModel->blockSignals(
true );
474 if ( deleteExisting )
477 mModel->blockSignals(
false );
481 void QgsPalettedRendererWidget::gatheredClasses()
483 if ( !mGatherer || mGatherer->wasCanceled() )
486 mModel->setClassData( mGatherer->classes() );
490 void QgsPalettedRendererWidget::gathererThreadFinished()
492 mGatherer->deleteLater();
494 mClassifyButton->setText( tr(
"Classify" ) );
495 mClassifyButton->setEnabled(
true );
496 mCalculatingProgressBar->hide();
497 mCancelButton->hide();
500 void QgsPalettedRendererWidget::layerWillBeRemoved(
QgsMapLayer *layer )
514 QgsPalettedRendererModel::QgsPalettedRendererModel( QObject *parent )
515 : QAbstractItemModel( parent )
527 QModelIndex QgsPalettedRendererModel::index(
int row,
int column,
const QModelIndex &parent )
const 529 if ( column < 0 || column >= columnCount() )
532 return QModelIndex();
535 if ( !parent.isValid() && row >= 0 && row < mData.size() )
538 return createIndex( row, column );
542 return QModelIndex();
545 QModelIndex QgsPalettedRendererModel::parent(
const QModelIndex &index )
const 550 return QModelIndex();
553 int QgsPalettedRendererModel::columnCount(
const QModelIndex &parent )
const 555 if ( parent.isValid() )
561 int QgsPalettedRendererModel::rowCount(
const QModelIndex &parent )
const 563 if ( parent.isValid() )
566 return mData.count();
569 QVariant QgsPalettedRendererModel::data(
const QModelIndex &index,
int role )
const 571 if ( !index.isValid() )
576 case Qt::DisplayRole:
579 switch ( index.column() )
582 return mData.at( index.row() ).value;
585 return mData.at( index.row() ).color;
588 return mData.at( index.row() ).label;
599 QVariant QgsPalettedRendererModel::headerData(
int section, Qt::Orientation orientation,
int role )
const 601 switch ( orientation )
610 case Qt::DisplayRole:
615 return tr(
"Value" );
618 return tr(
"Color" );
621 return tr(
"Label" );
630 return QAbstractItemModel::headerData( section, orientation, role );
632 return QAbstractItemModel::headerData( section, orientation, role );
635 bool QgsPalettedRendererModel::setData(
const QModelIndex &index,
const QVariant &value,
int )
637 if ( !index.isValid() )
639 if ( index.row() >= mData.length() )
642 switch ( index.column() )
647 int newValue = value.toInt( &ok );
651 mData[ index.row() ].value = newValue;
652 emit dataChanged( index, index );
653 emit classesChanged();
659 mData[ index.row() ].color = value.value<QColor>();
660 emit dataChanged( index, index );
661 emit classesChanged();
667 mData[ index.row() ].label = value.toString();
668 emit dataChanged( index, index );
669 emit classesChanged();
677 Qt::ItemFlags QgsPalettedRendererModel::flags(
const QModelIndex &index )
const 679 if ( !index.isValid() )
680 return QAbstractItemModel::flags( index ) | Qt::ItemIsDropEnabled;
682 Qt::ItemFlags f = QAbstractItemModel::flags( index );
683 switch ( index.column() )
688 f = f | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
691 return f | Qt::ItemIsEnabled | Qt::ItemIsSelectable;;
694 bool QgsPalettedRendererModel::removeRows(
int row,
int count,
const QModelIndex &parent )
696 if ( row < 0 || row >= mData.count() )
698 if ( parent.isValid() )
701 for (
int i = row + count - 1; i >= row; --i )
703 beginRemoveRows( parent, i, i );
707 emit classesChanged();
711 bool QgsPalettedRendererModel::insertRows(
int row,
int count,
const QModelIndex & )
713 QgsPalettedRasterRenderer::ClassData::const_iterator cIt = mData.constBegin();
714 int currentMaxValue = -std::numeric_limits<int>::max();
715 for ( ; cIt != mData.constEnd(); ++cIt )
717 int value = cIt->value;
718 currentMaxValue = std::max( value, currentMaxValue );
720 int nextValue = std::max( 0, currentMaxValue + 1 );
722 beginInsertRows( QModelIndex(), row, row + count - 1 );
723 for (
int i = row; i < row + count; ++i, ++nextValue )
728 emit classesChanged();
732 Qt::DropActions QgsPalettedRendererModel::supportedDropActions()
const 734 return Qt::MoveAction;
737 QStringList QgsPalettedRendererModel::mimeTypes()
const 740 types << QStringLiteral(
"application/x-qgspalettedrenderermodel" );
744 QMimeData *QgsPalettedRendererModel::mimeData(
const QModelIndexList &indexes )
const 746 QMimeData *mimeData =
new QMimeData();
747 QByteArray encodedData;
749 QDataStream stream( &encodedData, QIODevice::WriteOnly );
752 const auto constIndexes = indexes;
753 for (
const QModelIndex &index : constIndexes )
755 if ( !index.isValid() || index.column() != 0 )
758 stream << index.row();
760 mimeData->setData( QStringLiteral(
"application/x-qgspalettedrenderermodel" ), encodedData );
764 bool QgsPalettedRendererModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex & )
767 if ( action != Qt::MoveAction )
return true;
769 if ( !data->hasFormat( QStringLiteral(
"application/x-qgspalettedrenderermodel" ) ) )
772 QByteArray encodedData = data->data( QStringLiteral(
"application/x-qgspalettedrenderermodel" ) );
773 QDataStream stream( &encodedData, QIODevice::ReadOnly );
776 while ( !stream.atEnd() )
784 for (
int i = 0; i < rows.count(); ++i )
785 newData << mData.at( rows.at( i ) );
790 beginInsertRows( QModelIndex(), row, row + rows.count() - 1 );
791 for (
int i = 0; i < rows.count(); ++i )
792 mData.insert( row + i, newData.at( i ) );
794 emit classesChanged();
798 QModelIndex QgsPalettedRendererModel::addEntry(
const QColor &color )
800 insertRow( rowCount() );
801 QModelIndex newRow = index( mData.count() - 1, 1 );
802 setData( newRow, color );
806 void QgsPalettedRendererModel::deleteAll()
811 emit classesChanged();
814 void QgsPalettedRendererClassGatherer::run()
816 mWasCanceled =
false;
825 QgsPalettedRasterRenderer::ClassData::iterator classIt = newClasses.begin();
826 for ( ; classIt != newClasses.end(); ++classIt )
831 if ( existingClass.value == classIt->value )
833 classIt->color = existingClass.color;
834 classIt->label = existingClass.label;
839 mClasses = newClasses;
842 mFeedbackMutex.lock();
845 mFeedbackMutex.unlock();
847 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:
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, it may be nullptr.
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)
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.