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 ) ) ) );