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: 
 
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 null. 
 
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.