29 #include <QColorDialog>
30 #include <QInputDialog>
31 #include <QFileDialog>
32 #include <QMessageBox>
35 #include <QTextStream>
37 #ifdef ENABLE_MODELTEST
38 #include "modeltest.h"
46 mCalculatingProgressBar->hide();
47 mCancelButton->hide();
49 mContextMenu =
new QMenu( tr(
"Options" ),
this );
50 mContextMenu->addAction( tr(
"Change Color…" ),
this, SLOT( changeColor() ) );
51 mContextMenu->addAction( tr(
"Change Opacity…" ),
this, SLOT( changeOpacity() ) );
52 mContextMenu->addAction( tr(
"Change Label…" ),
this, SLOT( changeLabel() ) );
54 mAdvancedMenu =
new QMenu( tr(
"Advanced Options" ),
this );
55 QAction *mLoadFromLayerAction = mAdvancedMenu->addAction( tr(
"Load Classes from Layer" ) );
56 connect( mLoadFromLayerAction, &QAction::triggered,
this, &QgsPalettedRendererWidget::loadFromLayer );
57 QAction *loadFromFile = mAdvancedMenu->addAction( tr(
"Load Color Map from File…" ) );
58 connect( loadFromFile, &QAction::triggered,
this, &QgsPalettedRendererWidget::loadColorTable );
59 QAction *exportToFile = mAdvancedMenu->addAction( tr(
"Export Color Map to File…" ) );
60 connect( exportToFile, &QAction::triggered,
this, &QgsPalettedRendererWidget::saveColorTable );
63 mButtonAdvanced->setMenu( mAdvancedMenu );
65 mModel =
new QgsPalettedRendererModel(
this );
66 mProxyModel =
new QgsPalettedRendererProxyModel(
this );
67 mProxyModel->setSourceModel( mModel );
68 mTreeView->setSortingEnabled(
false );
69 mTreeView->setModel( mProxyModel );
73 mProxyModel->sort( QgsPalettedRendererModel::Column::ValueColumn );
76 #ifdef ENABLE_MODELTEST
77 new ModelTest( mModel,
this );
80 mTreeView->setItemDelegateForColumn( QgsPalettedRendererModel::ColorColumn,
new QgsColorSwatchDelegate(
this ) );
82 mTreeView->setItemDelegateForColumn( QgsPalettedRendererModel::ValueColumn, mValueDelegate );
84 mTreeView->setColumnWidth( QgsPalettedRendererModel::ColorColumn,
Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance(
'X' ) * 6.6 );
85 mTreeView->setContextMenuPolicy( Qt::CustomContextMenu );
86 mTreeView->setSelectionMode( QAbstractItemView::ExtendedSelection );
87 mTreeView->setDragEnabled(
true );
88 mTreeView->setAcceptDrops(
true );
89 mTreeView->setDropIndicatorShown(
true );
90 mTreeView->setDragDropMode( QAbstractItemView::InternalMove );
91 mTreeView->setSelectionBehavior( QAbstractItemView::SelectRows );
92 mTreeView->setDefaultDropAction( Qt::MoveAction );
94 connect( mTreeView, &QTreeView::customContextMenuRequested,
this, [ = ]( QPoint ) { mContextMenu->exec( QCursor::pos() ); } );
96 btnColorRamp->setShowRandomColorRamp(
true );
114 connect( mDeleteEntryButton, &QPushButton::clicked,
this, &QgsPalettedRendererWidget::deleteEntry );
115 connect( mButtonDeleteAll, &QPushButton::clicked, mModel, &QgsPalettedRendererModel::deleteAll );
116 connect( mAddEntryButton, &QPushButton::clicked,
this, &QgsPalettedRendererWidget::addEntry );
117 connect( mClassifyButton, &QPushButton::clicked,
this, &QgsPalettedRendererWidget::classify );
125 mLoadFromLayerAction->setEnabled(
false );
144 int bandNumber = mBandComboBox->currentBand();
147 if ( !btnColorRamp->isNull() )
163 mModel->setClassData( pr->
classes() );
188 void QgsPalettedRendererWidget::setSelectionColor(
const QItemSelection &selection,
const QColor &color )
193 QModelIndex colorIndex;
194 const auto constSelection = selection;
195 for (
const QItemSelectionRange &range : constSelection )
197 const auto constIndexes = range.indexes();
198 for (
const QModelIndex &index : constIndexes )
200 colorIndex = mModel->index( index.row(), QgsPalettedRendererModel::ColorColumn );
201 mModel->setData( colorIndex, color, Qt::EditRole );
209 void QgsPalettedRendererWidget::deleteEntry()
214 QItemSelection sel = mProxyModel->mapSelectionToSource( mTreeView->selectionModel()->selection() );
215 const auto constSel = sel;
216 for (
const QItemSelectionRange &range : constSel )
218 if ( range.isValid() )
219 mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
227 void QgsPalettedRendererWidget::addEntry()
231 QColor color( 150, 150, 150 );
232 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
235 color = ramp->color( 1.0 );
237 QModelIndex newEntry = mModel->addEntry( color );
238 mTreeView->scrollTo( newEntry );
239 mTreeView->selectionModel()->select( mProxyModel->mapFromSource( newEntry ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
244 void QgsPalettedRendererWidget::changeColor()
246 QItemSelection sel = mProxyModel->mapSelectionToSource( mTreeView->selectionModel()->selection() );
247 QModelIndex colorIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::ColorColumn );
248 QColor currentColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
251 if ( panel && panel->dockMode() )
257 panel->openPanel( colorWidget );
263 if ( newColor.isValid() )
265 setSelectionColor( sel, newColor );
270 void QgsPalettedRendererWidget::changeOpacity()
272 QItemSelection sel = mProxyModel->mapSelectionToSource( mTreeView->selectionModel()->selection() );
273 QModelIndex colorIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::ColorColumn );
274 QColor currentColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
277 double oldOpacity = ( currentColor.alpha() / 255.0 ) * 100.0;
278 double opacity = QInputDialog::getDouble(
this, tr(
"Opacity" ), tr(
"Change color opacity [%]" ), oldOpacity, 0.0, 100.0, 0, &ok );
281 int newOpacity = opacity / 100 * 255;
286 const auto constSel = sel;
287 for (
const QItemSelectionRange &range : constSel )
289 const auto constIndexes = range.indexes();
290 for (
const QModelIndex &index : constIndexes )
292 colorIndex = mModel->index( index.row(), QgsPalettedRendererModel::ColorColumn );
294 QColor newColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
295 newColor.setAlpha( newOpacity );
296 mModel->setData( colorIndex, newColor, Qt::EditRole );
305 void QgsPalettedRendererWidget::changeLabel()
307 QItemSelection sel = mProxyModel->mapSelectionToSource( mTreeView->selectionModel()->selection() );
308 QModelIndex labelIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::LabelColumn );
309 QString currentLabel = mModel->data( labelIndex, Qt::DisplayRole ).toString();
312 QString newLabel = QInputDialog::getText(
this, tr(
"Label" ), tr(
"Change label" ), QLineEdit::Normal, currentLabel, &ok );
318 const auto constSel = sel;
319 for (
const QItemSelectionRange &range : constSel )
321 const auto constIndexes = range.indexes();
322 for (
const QModelIndex &index : constIndexes )
324 labelIndex = mModel->index( index.row(), QgsPalettedRendererModel::LabelColumn );
325 mModel->setData( labelIndex, newLabel, Qt::EditRole );
334 void QgsPalettedRendererWidget::applyColorRamp()
336 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
345 QgsPalettedRasterRenderer::ClassData::iterator cIt = data.begin();
347 double numberOfEntries = data.count();
354 randomRamp->setTotalColorCount( numberOfEntries );
357 if ( numberOfEntries > 1 )
358 numberOfEntries -= 1;
360 for ( ; cIt != data.end(); ++cIt )
362 cIt->color = ramp->color( i / numberOfEntries );
365 mModel->setClassData( data );
371 void QgsPalettedRendererWidget::loadColorTable()
374 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
375 QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Color Table from File" ), lastDir );
376 if ( !fileName.isEmpty() )
379 if ( !classes.isEmpty() )
382 mModel->setClassData( classes );
388 QMessageBox::critical(
nullptr, tr(
"Load Color Table" ), tr(
"Could not interpret file as a raster color table." ) );
393 void QgsPalettedRendererWidget::saveColorTable()
396 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
397 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Color Table as File" ), lastDir, tr(
"Text (*.clr)" ) );
398 if ( !fileName.isEmpty() )
400 if ( !fileName.endsWith( QLatin1String(
".clr" ), Qt::CaseInsensitive ) )
402 fileName = fileName +
".clr";
405 QFile outputFile( fileName );
406 if ( outputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
408 QTextStream outputStream( &outputFile );
410 outputStream.flush();
413 QFileInfo fileInfo( fileName );
414 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
418 QMessageBox::warning(
this, tr(
"Save Color Table as File" ), tr(
"Write access denied. Adjust the file permissions and try again.\n\n" ) );
423 void QgsPalettedRendererWidget::classify()
439 mGatherer =
new QgsPalettedRendererClassGatherer(
mRasterLayer, mBandComboBox->currentBand(), mModel->classData(), btnColorRamp->colorRamp() );
441 connect( mGatherer, &QgsPalettedRendererClassGatherer::progressChanged, mCalculatingProgressBar, [ = ](
int progress )
443 mCalculatingProgressBar->setValue( progress );
446 mCalculatingProgressBar->show();
447 mCancelButton->show();
448 connect( mCancelButton, &QPushButton::clicked, mGatherer, &QgsPalettedRendererClassGatherer::stop );
450 connect( mGatherer, &QgsPalettedRendererClassGatherer::collectedClasses,
this, &QgsPalettedRendererWidget::gatheredClasses );
451 connect( mGatherer, &QgsPalettedRendererClassGatherer::finished,
this, &QgsPalettedRendererWidget::gathererThreadFinished );
452 mClassifyButton->setText( tr(
"Calculating…" ) );
453 mClassifyButton->setEnabled(
false );
458 void QgsPalettedRendererWidget::loadFromLayer()
464 QList<QgsColorRampShader::ColorRampItem> table = provider->
colorTable( mBandComboBox->currentBand() );
465 if ( !table.isEmpty() )
468 mModel->setClassData( classes );
474 void QgsPalettedRendererWidget::bandChanged(
int band )
484 bool deleteExisting =
false;
485 if ( !mModel->classData().isEmpty() )
487 int res = QMessageBox::question(
this,
488 tr(
"Delete Classification" ),
489 tr(
"The classification band was changed from %1 to %2.\n"
490 "Should the existing classes be deleted?" ).arg( mBand ).arg( band ),
491 QMessageBox::Yes | QMessageBox::No );
493 deleteExisting = ( res == QMessageBox::Yes );
497 mModel->blockSignals(
true );
498 if ( deleteExisting )
501 mModel->blockSignals(
false );
505 void QgsPalettedRendererWidget::gatheredClasses()
507 if ( !mGatherer || mGatherer->wasCanceled() )
510 mModel->setClassData( mGatherer->classes() );
514 void QgsPalettedRendererWidget::gathererThreadFinished()
516 mGatherer->deleteLater();
518 mClassifyButton->setText( tr(
"Classify" ) );
519 mClassifyButton->setEnabled(
true );
520 mCalculatingProgressBar->hide();
521 mCancelButton->hide();
524 void QgsPalettedRendererWidget::layerWillBeRemoved(
QgsMapLayer *layer )
538 QgsPalettedRendererModel::QgsPalettedRendererModel( QObject *parent )
539 : QAbstractItemModel( parent )
551 QModelIndex QgsPalettedRendererModel::index(
int row,
int column,
const QModelIndex &parent )
const
553 if ( column < 0 || column >= columnCount() )
556 return QModelIndex();
559 if ( !parent.isValid() && row >= 0 && row < mData.size() )
562 return createIndex( row, column );
566 return QModelIndex();
569 QModelIndex QgsPalettedRendererModel::parent(
const QModelIndex &index )
const
574 return QModelIndex();
577 int QgsPalettedRendererModel::columnCount(
const QModelIndex &parent )
const
579 if ( parent.isValid() )
585 int QgsPalettedRendererModel::rowCount(
const QModelIndex &parent )
const
587 if ( parent.isValid() )
590 return mData.count();
593 QVariant QgsPalettedRendererModel::data(
const QModelIndex &index,
int role )
const
595 if ( !index.isValid() )
600 case Qt::DisplayRole:
603 switch ( index.column() )
606 return mData.at( index.row() ).value;
609 return mData.at( index.row() ).color;
612 return mData.at( index.row() ).label;
623 QVariant QgsPalettedRendererModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
625 switch ( orientation )
634 case Qt::DisplayRole:
639 return tr(
"Value" );
642 return tr(
"Color" );
645 return tr(
"Label" );
654 return QAbstractItemModel::headerData( section, orientation, role );
656 return QAbstractItemModel::headerData( section, orientation, role );
659 bool QgsPalettedRendererModel::setData(
const QModelIndex &index,
const QVariant &value,
int )
661 if ( !index.isValid() )
663 if ( index.row() >= mData.length() )
666 switch ( index.column() )
671 double newValue = value.toDouble( &ok );
675 mData[ index.row() ].value = newValue;
676 emit dataChanged( index, index );
677 emit classesChanged();
683 mData[ index.row() ].color = value.value<QColor>();
684 emit dataChanged( index, index );
685 emit classesChanged();
691 mData[ index.row() ].label = value.toString();
692 emit dataChanged( index, index );
693 emit classesChanged();
701 Qt::ItemFlags QgsPalettedRendererModel::flags(
const QModelIndex &index )
const
703 if ( !index.isValid() )
704 return QAbstractItemModel::flags( index ) | Qt::ItemIsDropEnabled;
706 Qt::ItemFlags f = QAbstractItemModel::flags( index );
707 switch ( index.column() )
712 f = f | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
715 return f | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
718 bool QgsPalettedRendererModel::removeRows(
int row,
int count,
const QModelIndex &parent )
720 if ( row < 0 || row >= mData.count() )
722 if ( parent.isValid() )
725 for (
int i = row + count - 1; i >= row; --i )
727 beginRemoveRows( parent, i, i );
731 emit classesChanged();
735 bool QgsPalettedRendererModel::insertRows(
int row,
int count,
const QModelIndex & )
737 QgsPalettedRasterRenderer::ClassData::const_iterator cIt = mData.constBegin();
738 int currentMaxValue = -std::numeric_limits<int>::max();
739 for ( ; cIt != mData.constEnd(); ++cIt )
741 int value = cIt->value;
742 currentMaxValue = std::max( value, currentMaxValue );
744 int nextValue = std::max( 0, currentMaxValue + 1 );
746 beginInsertRows( QModelIndex(), row, row + count - 1 );
747 for (
int i = row; i < row + count; ++i, ++nextValue )
752 emit classesChanged();
756 Qt::DropActions QgsPalettedRendererModel::supportedDropActions()
const
758 return Qt::MoveAction;
761 QStringList QgsPalettedRendererModel::mimeTypes()
const
764 types << QStringLiteral(
"application/x-qgspalettedrenderermodel" );
768 QMimeData *QgsPalettedRendererModel::mimeData(
const QModelIndexList &indexes )
const
770 QMimeData *mimeData =
new QMimeData();
771 QByteArray encodedData;
773 QDataStream stream( &encodedData, QIODevice::WriteOnly );
776 const auto constIndexes = indexes;
777 for (
const QModelIndex &index : constIndexes )
779 if ( !index.isValid() || index.column() != 0 )
782 stream << index.row();
784 mimeData->setData( QStringLiteral(
"application/x-qgspalettedrenderermodel" ), encodedData );
788 bool QgsPalettedRendererModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex & )
791 if ( action != Qt::MoveAction )
return true;
793 if ( !data->hasFormat( QStringLiteral(
"application/x-qgspalettedrenderermodel" ) ) )
796 QByteArray encodedData = data->data( QStringLiteral(
"application/x-qgspalettedrenderermodel" ) );
797 QDataStream stream( &encodedData, QIODevice::ReadOnly );
800 while ( !stream.atEnd() )
808 for (
int i = 0; i < rows.count(); ++i )
809 newData << mData.at( rows.at( i ) );
814 beginInsertRows( QModelIndex(), row, row + rows.count() - 1 );
815 for (
int i = 0; i < rows.count(); ++i )
816 mData.insert( row + i, newData.at( i ) );
818 emit classesChanged();
822 QModelIndex QgsPalettedRendererModel::addEntry(
const QColor &color )
824 insertRow( rowCount() );
825 QModelIndex newRow = index( mData.count() - 1, 1 );
826 setData( newRow, color );
830 void QgsPalettedRendererModel::deleteAll()
835 emit classesChanged();
844 , mBandNumber( bandNumber )
846 , mClasses( existingClasses )
847 , mWasCanceled( false )
850 void QgsPalettedRendererClassGatherer::run()
852 mWasCanceled =
false;
861 QgsPalettedRasterRenderer::ClassData::iterator classIt = newClasses.begin();
862 emit progressChanged( 0 );
864 for ( ; classIt != newClasses.end(); ++classIt )
869 if ( existingClass.value == classIt->value )
871 classIt->color = existingClass.color;
872 classIt->label = existingClass.label;
877 emit progressChanged( 100 * ( i /
static_cast<float>( newClasses.count() ) ) );
879 mClasses = newClasses;
882 mFeedbackMutex.lock();
885 mFeedbackMutex.unlock();
887 emit collectedClasses();
894 for (
int i = 0; i < rowCount( ); ++i )
896 data.push_back( qobject_cast<QgsPalettedRendererModel *>( sourceModel() )->classAtIndex( mapToSource( index( i, 0 ) ) ) );