23 #include "qgssettings.h" 
   28 #include <QColorDialog> 
   29 #include <QInputDialog> 
   30 #include <QFileDialog> 
   31 #include <QMessageBox> 
   34 #include <QTextStream> 
   36 #ifdef ENABLE_MODELTEST 
   37 #include "modeltest.h" 
   45   mCalculatingProgressBar->hide();
 
   46   mCancelButton->hide();
 
   48   mContextMenu = 
new QMenu( tr( 
"Options" ), 
this );
 
   49   mContextMenu->addAction( tr( 
"Change Color…" ), 
this, SLOT( changeColor() ) );
 
   50   mContextMenu->addAction( tr( 
"Change Opacity…" ), 
this, SLOT( changeOpacity() ) );
 
   51   mContextMenu->addAction( tr( 
"Change Label…" ), 
this, SLOT( changeLabel() ) );
 
   53   mAdvancedMenu = 
new QMenu( tr( 
"Advanced Options" ), 
this );
 
   54   QAction *mLoadFromLayerAction = mAdvancedMenu->addAction( tr( 
"Load Classes from Layer" ) );
 
   55   connect( mLoadFromLayerAction, &QAction::triggered, 
this, &QgsPalettedRendererWidget::loadFromLayer );
 
   56   QAction *loadFromFile = mAdvancedMenu->addAction( tr( 
"Load Color Map from File…" ) );
 
   57   connect( loadFromFile, &QAction::triggered, 
this, &QgsPalettedRendererWidget::loadColorTable );
 
   58   QAction *exportToFile = mAdvancedMenu->addAction( tr( 
"Export Color Map to File…" ) );
 
   59   connect( exportToFile, &QAction::triggered, 
this, &QgsPalettedRendererWidget::saveColorTable );
 
   62   mButtonAdvanced->setMenu( mAdvancedMenu );
 
   64   mModel = 
new QgsPalettedRendererModel( 
this );
 
   65   mProxyModel = 
new QgsPalettedRendererProxyModel( 
this );
 
   66   mProxyModel->setSourceModel( mModel );
 
   67   mTreeView->setSortingEnabled( 
false );
 
   68   mTreeView->setModel( mProxyModel );
 
   72     mProxyModel->sort( QgsPalettedRendererModel::Column::ValueColumn );
 
   75 #ifdef ENABLE_MODELTEST 
   76   new ModelTest( mModel, 
this );
 
   79   mTreeView->setItemDelegateForColumn( QgsPalettedRendererModel::ColorColumn, 
new QgsColorSwatchDelegate( 
this ) );
 
   81   mTreeView->setItemDelegateForColumn( QgsPalettedRendererModel::ValueColumn, mValueDelegate );
 
   83   mTreeView->setColumnWidth( QgsPalettedRendererModel::ColorColumn, 
Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 
'X' ) * 6.6 );
 
   84   mTreeView->setContextMenuPolicy( Qt::CustomContextMenu );
 
   85   mTreeView->setSelectionMode( QAbstractItemView::ExtendedSelection );
 
   86   mTreeView->setDragEnabled( 
true );
 
   87   mTreeView->setAcceptDrops( 
true );
 
   88   mTreeView->setDropIndicatorShown( 
true );
 
   89   mTreeView->setDragDropMode( QAbstractItemView::InternalMove );
 
   90   mTreeView->setSelectionBehavior( QAbstractItemView::SelectRows );
 
   91   mTreeView->setDefaultDropAction( Qt::MoveAction );
 
   93   connect( mTreeView, &QTreeView::customContextMenuRequested, 
this, [ = ]( QPoint ) { mContextMenu->exec( QCursor::pos() ); } );
 
   95   btnColorRamp->setShowRandomColorRamp( 
true );
 
  113   connect( mDeleteEntryButton, &QPushButton::clicked, 
this, &QgsPalettedRendererWidget::deleteEntry );
 
  114   connect( mButtonDeleteAll, &QPushButton::clicked, mModel, &QgsPalettedRendererModel::deleteAll );
 
  115   connect( mAddEntryButton, &QPushButton::clicked, 
this, &QgsPalettedRendererWidget::addEntry );
 
  116   connect( mClassifyButton, &QPushButton::clicked, 
this, &QgsPalettedRendererWidget::classify );
 
  124     mLoadFromLayerAction->setEnabled( 
false );
 
  143   int bandNumber = mBandComboBox->currentBand();
 
  146   if ( !btnColorRamp->isNull() )
 
  162     mModel->setClassData( pr->
classes() );
 
  187 void QgsPalettedRendererWidget::setSelectionColor( 
const QItemSelection &selection, 
const QColor &color )
 
  192   QModelIndex colorIndex;
 
  193   const auto constSelection = selection;
 
  194   for ( 
const QItemSelectionRange &range : constSelection )
 
  196     const auto constIndexes = range.indexes();
 
  197     for ( 
const QModelIndex &index : constIndexes )
 
  199       colorIndex = mModel->index( index.row(), QgsPalettedRendererModel::ColorColumn );
 
  200       mModel->setData( colorIndex, color, Qt::EditRole );
 
  208 void QgsPalettedRendererWidget::deleteEntry()
 
  213   QItemSelection sel = mProxyModel->mapSelectionToSource( mTreeView->selectionModel()->selection() );
 
  214   const auto constSel = sel;
 
  215   for ( 
const QItemSelectionRange &range : constSel )
 
  217     if ( range.isValid() )
 
  218       mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
 
  226 void QgsPalettedRendererWidget::addEntry()
 
  230   QColor color( 150, 150, 150 );
 
  231   std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
 
  234     color = ramp->color( 1.0 );
 
  236   QModelIndex newEntry = mModel->addEntry( color );
 
  237   mTreeView->scrollTo( newEntry );
 
  238   mTreeView->selectionModel()->select( mProxyModel->mapFromSource( newEntry ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
 
  243 void QgsPalettedRendererWidget::changeColor()
 
  245   QItemSelection sel = mProxyModel->mapSelectionToSource( mTreeView->selectionModel()->selection() );
 
  246   QModelIndex colorIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::ColorColumn );
 
  247   QColor currentColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
 
  250   if ( panel && panel->dockMode() )
 
  256     panel->openPanel( colorWidget );
 
  262     if ( newColor.isValid() )
 
  264       setSelectionColor( sel, newColor );
 
  269 void QgsPalettedRendererWidget::changeOpacity()
 
  271   QItemSelection sel = mProxyModel->mapSelectionToSource( mTreeView->selectionModel()->selection() );
 
  272   QModelIndex colorIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::ColorColumn );
 
  273   QColor currentColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
 
  276   double oldOpacity = ( currentColor.alpha() / 255.0 ) * 100.0;
 
  277   double opacity = QInputDialog::getDouble( 
this, tr( 
"Opacity" ), tr( 
"Change color opacity [%]" ), oldOpacity, 0.0, 100.0, 0, &ok );
 
  280     int newOpacity = opacity / 100 * 255;
 
  285     const auto constSel = sel;
 
  286     for ( 
const QItemSelectionRange &range : constSel )
 
  288       const auto constIndexes = range.indexes();
 
  289       for ( 
const QModelIndex &index : constIndexes )
 
  291         colorIndex = mModel->index( index.row(), QgsPalettedRendererModel::ColorColumn );
 
  293         QColor newColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
 
  294         newColor.setAlpha( newOpacity );
 
  295         mModel->setData( colorIndex, newColor, Qt::EditRole );
 
  304 void QgsPalettedRendererWidget::changeLabel()
 
  306   QItemSelection sel = mProxyModel->mapSelectionToSource( mTreeView->selectionModel()->selection() );
 
  307   QModelIndex labelIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::LabelColumn );
 
  308   QString currentLabel = mModel->data( labelIndex, Qt::DisplayRole ).toString();
 
  311   QString newLabel = QInputDialog::getText( 
this, tr( 
"Label" ), tr( 
"Change label" ), QLineEdit::Normal, currentLabel, &ok );
 
  317     const auto constSel = sel;
 
  318     for ( 
const QItemSelectionRange &range : constSel )
 
  320       const auto constIndexes = range.indexes();
 
  321       for ( 
const QModelIndex &index : constIndexes )
 
  323         labelIndex = mModel->index( index.row(), QgsPalettedRendererModel::LabelColumn );
 
  324         mModel->setData( labelIndex, newLabel, Qt::EditRole );
 
  333 void QgsPalettedRendererWidget::applyColorRamp()
 
  335   std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
 
  344   QgsPalettedRasterRenderer::ClassData::iterator cIt = data.begin();
 
  346   double numberOfEntries = data.count();
 
  353     randomRamp->setTotalColorCount( numberOfEntries );
 
  356   if ( numberOfEntries > 1 )
 
  357     numberOfEntries -= 1; 
 
  359   for ( ; cIt != data.end(); ++cIt )
 
  361     cIt->color = ramp->color( i / numberOfEntries );
 
  364   mModel->setClassData( data );
 
  370 void QgsPalettedRendererWidget::loadColorTable()
 
  372   QgsSettings settings;
 
  373   QString lastDir = settings.value( QStringLiteral( 
"lastColorMapDir" ), QDir::homePath() ).toString();
 
  374   QString fileName = QFileDialog::getOpenFileName( 
this, tr( 
"Load Color Table from File" ), lastDir );
 
  375   if ( !fileName.isEmpty() )
 
  378     if ( !classes.isEmpty() )
 
  381       mModel->setClassData( classes );
 
  387       QMessageBox::critical( 
nullptr, tr( 
"Load Color Table" ), tr( 
"Could not interpret file as a raster color table." ) );
 
  392 void QgsPalettedRendererWidget::saveColorTable()
 
  394   QgsSettings settings;
 
  395   QString lastDir = settings.value( QStringLiteral( 
"lastColorMapDir" ), QDir::homePath() ).toString();
 
  396   QString fileName = QFileDialog::getSaveFileName( 
this, tr( 
"Save Color Table as File" ), lastDir, tr( 
"Text (*.clr)" ) );
 
  397   if ( !fileName.isEmpty() )
 
  399     if ( !fileName.endsWith( QLatin1String( 
".clr" ), Qt::CaseInsensitive ) )
 
  401       fileName = fileName + 
".clr";
 
  404     QFile outputFile( fileName );
 
  405     if ( outputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
 
  407       QTextStream outputStream( &outputFile );
 
  409       outputStream.flush();
 
  412       QFileInfo fileInfo( fileName );
 
  413       settings.setValue( QStringLiteral( 
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
 
  417       QMessageBox::warning( 
this, tr( 
"Save Color Table as File" ), tr( 
"Write access denied. Adjust the file permissions and try again.\n\n" ) );
 
  422 void QgsPalettedRendererWidget::classify()
 
  438     mGatherer = 
new QgsPalettedRendererClassGatherer( 
mRasterLayer, mBandComboBox->currentBand(), mModel->classData(), btnColorRamp->colorRamp() );
 
  440     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 );
 
  457 void QgsPalettedRendererWidget::loadFromLayer()
 
  463     QList<QgsColorRampShader::ColorRampItem> table = provider->
colorTable( mBandComboBox->currentBand() );
 
  464     if ( !table.isEmpty() )
 
  467       mModel->setClassData( classes );
 
  473 void QgsPalettedRendererWidget::bandChanged( 
int band )
 
  483   bool deleteExisting = 
false;
 
  484   if ( !mModel->classData().isEmpty() )
 
  486     int res = QMessageBox::question( 
this,
 
  487                                      tr( 
"Delete Classification" ),
 
  488                                      tr( 
"The classification band was changed from %1 to %2.\n" 
  489                                          "Should the existing classes be deleted?" ).arg( mBand ).arg( band ),
 
  490                                      QMessageBox::Yes | QMessageBox::No );
 
  492     deleteExisting = ( res == QMessageBox::Yes );
 
  496   mModel->blockSignals( 
true );
 
  497   if ( deleteExisting )
 
  500   mModel->blockSignals( 
false );
 
  504 void QgsPalettedRendererWidget::gatheredClasses()
 
  506   if ( !mGatherer || mGatherer->wasCanceled() )
 
  509   mModel->setClassData( mGatherer->classes() );
 
  513 void QgsPalettedRendererWidget::gathererThreadFinished()
 
  515   mGatherer->deleteLater();
 
  517   mClassifyButton->setText( tr( 
"Classify" ) );
 
  518   mClassifyButton->setEnabled( 
true );
 
  519   mCalculatingProgressBar->hide();
 
  520   mCancelButton->hide();
 
  523 void QgsPalettedRendererWidget::layerWillBeRemoved( 
QgsMapLayer *layer )
 
  537 QgsPalettedRendererModel::QgsPalettedRendererModel( QObject *parent )
 
  538   : QAbstractItemModel( parent )
 
  550 QModelIndex QgsPalettedRendererModel::index( 
int row, 
int column, 
const QModelIndex &parent )
 const 
  552   if ( column < 0 || column >= columnCount() )
 
  555     return QModelIndex();
 
  558   if ( !parent.isValid() && row >= 0 && row < mData.size() )
 
  561     return createIndex( row, column );
 
  565   return QModelIndex();
 
  568 QModelIndex QgsPalettedRendererModel::parent( 
const QModelIndex &index )
 const 
  573   return QModelIndex();
 
  576 int QgsPalettedRendererModel::columnCount( 
const QModelIndex &parent )
 const 
  578   if ( parent.isValid() )
 
  584 int QgsPalettedRendererModel::rowCount( 
const QModelIndex &parent )
 const 
  586   if ( parent.isValid() )
 
  589   return mData.count();
 
  592 QVariant QgsPalettedRendererModel::data( 
const QModelIndex &index, 
int role )
 const 
  594   if ( !index.isValid() )
 
  599     case Qt::DisplayRole:
 
  602       switch ( index.column() )
 
  605           return mData.at( index.row() ).value;
 
  608           return mData.at( index.row() ).color;
 
  611           return mData.at( index.row() ).label;
 
  622 QVariant QgsPalettedRendererModel::headerData( 
int section, Qt::Orientation orientation, 
int role )
 const 
  624   switch ( orientation )
 
  633         case Qt::DisplayRole:
 
  638               return tr( 
"Value" );
 
  641               return tr( 
"Color" );
 
  644               return tr( 
"Label" );
 
  653       return QAbstractItemModel::headerData( section, orientation, role );
 
  655   return QAbstractItemModel::headerData( section, orientation, role );
 
  658 bool QgsPalettedRendererModel::setData( 
const QModelIndex &index, 
const QVariant &value, 
int )
 
  660   if ( !index.isValid() )
 
  662   if ( index.row() >= mData.length() )
 
  665   switch ( index.column() )
 
  670       double newValue = value.toDouble( &ok );
 
  674       mData[ index.row() ].value = newValue;
 
  675       emit dataChanged( index, index );
 
  676       emit classesChanged();
 
  682       mData[ index.row() ].color = value.value<QColor>();
 
  683       emit dataChanged( index, index );
 
  684       emit classesChanged();
 
  690       mData[ index.row() ].label = value.toString();
 
  691       emit dataChanged( index, index );
 
  692       emit classesChanged();
 
  700 Qt::ItemFlags QgsPalettedRendererModel::flags( 
const QModelIndex &index )
 const 
  702   if ( !index.isValid() )
 
  703     return QAbstractItemModel::flags( index ) | Qt::ItemIsDropEnabled;
 
  705   Qt::ItemFlags f = QAbstractItemModel::flags( index );
 
  706   switch ( index.column() )
 
  711       f = f | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
 
  714   return f | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
 
  717 bool QgsPalettedRendererModel::removeRows( 
int row, 
int count, 
const QModelIndex &parent )
 
  719   if ( row < 0 || row >= mData.count() )
 
  721   if ( parent.isValid() )
 
  724   for ( 
int i = row + count - 1; i >= row; --i )
 
  726     beginRemoveRows( parent, i, i );
 
  730   emit classesChanged();
 
  734 bool QgsPalettedRendererModel::insertRows( 
int row, 
int count, 
const QModelIndex & )
 
  736   QgsPalettedRasterRenderer::ClassData::const_iterator cIt = mData.constBegin();
 
  737   int currentMaxValue = -std::numeric_limits<int>::max();
 
  738   for ( ; cIt != mData.constEnd(); ++cIt )
 
  740     int value = cIt->value;
 
  741     currentMaxValue = std::max( value, currentMaxValue );
 
  743   int nextValue = std::max( 0, currentMaxValue + 1 );
 
  745   beginInsertRows( QModelIndex(), row, row + count - 1 );
 
  746   for ( 
int i = row; i < row + count; ++i, ++nextValue )
 
  751   emit classesChanged();
 
  755 Qt::DropActions QgsPalettedRendererModel::supportedDropActions()
 const 
  757   return Qt::MoveAction;
 
  760 QStringList QgsPalettedRendererModel::mimeTypes()
 const 
  763   types << QStringLiteral( 
"application/x-qgspalettedrenderermodel" );
 
  767 QMimeData *QgsPalettedRendererModel::mimeData( 
const QModelIndexList &indexes )
 const 
  769   QMimeData *mimeData = 
new QMimeData();
 
  770   QByteArray encodedData;
 
  772   QDataStream stream( &encodedData, QIODevice::WriteOnly );
 
  775   const auto constIndexes = indexes;
 
  776   for ( 
const QModelIndex &index : constIndexes )
 
  778     if ( !index.isValid() || index.column() != 0 )
 
  781     stream << index.row();
 
  783   mimeData->setData( QStringLiteral( 
"application/x-qgspalettedrenderermodel" ), encodedData );
 
  787 bool QgsPalettedRendererModel::dropMimeData( 
const QMimeData *data, Qt::DropAction action, 
int row, 
int column, 
const QModelIndex & )
 
  790   if ( action != Qt::MoveAction ) 
return true;
 
  792   if ( !data->hasFormat( QStringLiteral( 
"application/x-qgspalettedrenderermodel" ) ) )
 
  795   QByteArray encodedData = data->data( QStringLiteral( 
"application/x-qgspalettedrenderermodel" ) );
 
  796   QDataStream stream( &encodedData, QIODevice::ReadOnly );
 
  799   while ( !stream.atEnd() )
 
  807   for ( 
int i = 0; i < rows.count(); ++i )
 
  808     newData << mData.at( rows.at( i ) );
 
  813   beginInsertRows( QModelIndex(), row, row + rows.count() - 1 );
 
  814   for ( 
int i = 0; i < rows.count(); ++i )
 
  815     mData.insert( row + i, newData.at( i ) );
 
  817   emit classesChanged();
 
  821 QModelIndex QgsPalettedRendererModel::addEntry( 
const QColor &color )
 
  823   insertRow( rowCount() );
 
  824   QModelIndex newRow = index( mData.count() - 1, 1 );
 
  825   setData( newRow, color );
 
  829 void QgsPalettedRendererModel::deleteAll()
 
  834   emit classesChanged();
 
  837 void QgsPalettedRendererClassGatherer::run()
 
  839   mWasCanceled = 
false;
 
  848   QgsPalettedRasterRenderer::ClassData::iterator classIt = newClasses.begin();
 
  849   emit progressChanged( 0 );
 
  851   for ( ; classIt != newClasses.end(); ++classIt )
 
  856       if ( existingClass.value == classIt->value )
 
  858         classIt->color = existingClass.color;
 
  859         classIt->label = existingClass.label;
 
  864     emit progressChanged( 100 * ( i / 
static_cast<float>( newClasses.count() ) ) );
 
  866   mClasses = newClasses;
 
  869   mFeedbackMutex.lock();
 
  872   mFeedbackMutex.unlock();
 
  874   emit collectedClasses();
 
  881   for ( 
int i = 0; i < rowCount( ); ++i )
 
  883     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.
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.
int band() const
Returns the raster band used for rendering the raster.
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.
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.
virtual QList< QgsColorRampShader::ColorRampItem > colorTable(int bandNo) const
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
Represents a raster layer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
QgsRasterRenderer * renderer() const
Returns the raster's renderer.
Raster renderer pipe that applies colors to a raster.
A rectangle specified with double values.
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.