QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgspalettedrendererwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspalettedrendererwidget.cpp
3  -----------------------------
4  begin : February 2012
5  copyright : (C) 2012 by Marco Hugentobler
6  email : marco at sourcepole dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
20 #include "qgsrasterdataprovider.h"
21 #include "qgsrasterlayer.h"
22 #include "qgscolordialog.h"
23 #include "qgssettings.h"
24 #include "qgsproject.h"
25 
26 #include <QColorDialog>
27 #include <QInputDialog>
28 #include <QFileDialog>
29 #include <QMessageBox>
30 #include <QMenu>
31 
32 #ifdef ENABLE_MODELTEST
33 #include "modeltest.h"
34 #endif
35 
37 {
38  setupUi( this );
39 
40  mCalculatingProgressBar->hide();
41  mCancelButton->hide();
42 
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() ) );
47 
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 );
55 
56 
57  mButtonAdvanced->setMenu( mAdvancedMenu );
58 
59  mModel = new QgsPalettedRendererModel( this );
60  mTreeView->setSortingEnabled( false );
61  mTreeView->setModel( mModel );
62 
63 #ifdef ENABLE_MODELTEST
64  new ModelTest( mModel, this );
65 #endif
66 
67  mSwatchDelegate = new QgsColorSwatchDelegate( this );
68  mTreeView->setItemDelegateForColumn( QgsPalettedRendererModel::ColorColumn, mSwatchDelegate );
69 
70 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
71  mTreeView->setColumnWidth( QgsPalettedRendererModel::ColorColumn, Qgis::UI_SCALE_FACTOR * fontMetrics().width( 'X' ) * 6.6 );
72 #else
73  mTreeView->setColumnWidth( QgsPalettedRendererModel::ColorColumn, Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 6.6 );
74 #endif
75  mTreeView->setContextMenuPolicy( Qt::CustomContextMenu );
76  mTreeView->setSelectionMode( QAbstractItemView::ExtendedSelection );
77  mTreeView->setDragEnabled( true );
78  mTreeView->setAcceptDrops( true );
79  mTreeView->setDropIndicatorShown( true );
80  mTreeView->setDragDropMode( QAbstractItemView::InternalMove );
81  mTreeView->setSelectionBehavior( QAbstractItemView::SelectRows );
82  mTreeView->setDefaultDropAction( Qt::MoveAction );
83 
84  connect( mTreeView, &QTreeView::customContextMenuRequested, this, [ = ]( QPoint ) { mContextMenu->exec( QCursor::pos() ); }
85  );
86 
87  btnColorRamp->setShowRandomColorRamp( true );
88 
89  connect( btnColorRamp, &QgsColorRampButton::colorRampChanged, this, &QgsPalettedRendererWidget::applyColorRamp );
90 
91  mBandComboBox->setLayer( mRasterLayer );
92 
93  if ( mRasterLayer )
94  {
96  if ( !provider )
97  {
98  return;
99  }
101  }
102 
104  connect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
105  connect( mDeleteEntryButton, &QPushButton::clicked, this, &QgsPalettedRendererWidget::deleteEntry );
106  connect( mButtonDeleteAll, &QPushButton::clicked, mModel, &QgsPalettedRendererModel::deleteAll );
107  connect( mAddEntryButton, &QPushButton::clicked, this, &QgsPalettedRendererWidget::addEntry );
108  connect( mClassifyButton, &QPushButton::clicked, this, &QgsPalettedRendererWidget::classify );
109 
111  if ( provider )
112  {
113  mLoadFromLayerAction->setEnabled( !provider->colorTable( mBandComboBox->currentBand() ).isEmpty() );
114  }
115  else
116  {
117  mLoadFromLayerAction->setEnabled( false );
118  }
119 
120  connect( QgsProject::instance(), static_cast < void ( QgsProject::* )( QgsMapLayer * ) >( &QgsProject::layerWillBeRemoved ), this, &QgsPalettedRendererWidget::layerWillBeRemoved );
121  connect( mBandComboBox, &QgsRasterBandComboBox::bandChanged, this, &QgsPalettedRendererWidget::bandChanged );
122 }
123 
125 {
126  if ( mGatherer )
127  {
128  mGatherer->stop();
129  mGatherer->wait(); // mGatherer is deleted when wait completes
130  }
131 }
132 
134 {
135  QgsPalettedRasterRenderer::ClassData classes = mModel->classData();
136  int bandNumber = mBandComboBox->currentBand();
137 
139  if ( !btnColorRamp->isNull() )
140  {
141  r->setSourceColorRamp( btnColorRamp->colorRamp() );
142  }
143  return r;
144 }
145 
147 {
148  const QgsPalettedRasterRenderer *pr = dynamic_cast<const QgsPalettedRasterRenderer *>( r );
149  if ( pr )
150  {
151  mBand = pr->band();
152  whileBlocking( mBandComboBox )->setBand( mBand );
153 
154  //read values and colors and fill into tree widget
155  mModel->setClassData( pr->classes() );
156 
157  if ( pr->sourceColorRamp() )
158  {
159  whileBlocking( btnColorRamp )->setColorRamp( pr->sourceColorRamp() );
160  }
161  else
162  {
163  std::unique_ptr< QgsColorRamp > ramp( new QgsRandomColorRamp() );
164  whileBlocking( btnColorRamp )->setColorRamp( ramp.get() );
165  }
166  }
167  else
168  {
169  loadFromLayer();
170  std::unique_ptr< QgsColorRamp > ramp( new QgsRandomColorRamp() );
171  whileBlocking( btnColorRamp )->setColorRamp( ramp.get() );
172  }
173 }
174 
175 void QgsPalettedRendererWidget::setSelectionColor( const QItemSelection &selection, const QColor &color )
176 {
177  // don't want to emit widgetChanged multiple times
178  disconnect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
179 
180  QModelIndex colorIndex;
181  const auto constSelection = selection;
182  for ( const QItemSelectionRange &range : constSelection )
183  {
184  const auto constIndexes = range.indexes();
185  for ( const QModelIndex &index : constIndexes )
186  {
187  colorIndex = mModel->index( index.row(), QgsPalettedRendererModel::ColorColumn );
188  mModel->setData( colorIndex, color, Qt::EditRole );
189  }
190  }
191  connect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
192 
193  emit widgetChanged();
194 }
195 
196 void QgsPalettedRendererWidget::deleteEntry()
197 {
198  // don't want to emit widgetChanged multiple times
199  disconnect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
200 
201  QItemSelection sel = mTreeView->selectionModel()->selection();
202  const auto constSel = sel;
203  for ( const QItemSelectionRange &range : constSel )
204  {
205  if ( range.isValid() )
206  mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
207  }
208 
209  connect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
210 
211  emit widgetChanged();
212 }
213 
214 void QgsPalettedRendererWidget::addEntry()
215 {
216  disconnect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
217 
218  QColor color( 150, 150, 150 );
219  std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
220  if ( ramp )
221  {
222  color = ramp->color( 1.0 );
223  }
224  QModelIndex newEntry = mModel->addEntry( color );
225  mTreeView->scrollTo( newEntry );
226  mTreeView->selectionModel()->select( newEntry, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
227  connect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
228  emit widgetChanged();
229 }
230 
231 void QgsPalettedRendererWidget::changeColor()
232 {
233  QItemSelection sel = mTreeView->selectionModel()->selection();
234 
235  QModelIndex colorIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::ColorColumn );
236  QColor currentColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
237 
238  QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( qobject_cast< QWidget * >( parent() ) );
239  if ( panel && panel->dockMode() )
240  {
242  colorWidget->setPanelTitle( tr( "Select Color" ) );
243  colorWidget->setAllowOpacity( true );
244  connect( colorWidget, &QgsCompoundColorWidget::currentColorChanged, this, [ = ]( const QColor & color ) { setSelectionColor( sel, color ); } );
245  panel->openPanel( colorWidget );
246  }
247  else
248  {
249  // modal dialog version... yuck
250  QColor newColor = QgsColorDialog::getColor( currentColor, this, QStringLiteral( "Change color" ), true );
251  if ( newColor.isValid() )
252  {
253  setSelectionColor( sel, newColor );
254  }
255  }
256 }
257 
258 void QgsPalettedRendererWidget::changeOpacity()
259 {
260  QItemSelection sel = mTreeView->selectionModel()->selection();
261 
262  QModelIndex colorIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::ColorColumn );
263  QColor currentColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
264 
265  bool ok;
266  double oldOpacity = ( currentColor.alpha() / 255.0 ) * 100.0;
267  double opacity = QInputDialog::getDouble( this, tr( "Opacity" ), tr( "Change color opacity [%]" ), oldOpacity, 0.0, 100.0, 0, &ok );
268  if ( ok )
269  {
270  int newOpacity = opacity / 100 * 255;
271 
272  // don't want to emit widgetChanged multiple times
273  disconnect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
274 
275  const auto constSel = sel;
276  for ( const QItemSelectionRange &range : constSel )
277  {
278  const auto constIndexes = range.indexes();
279  for ( const QModelIndex &index : constIndexes )
280  {
281  colorIndex = mModel->index( index.row(), QgsPalettedRendererModel::ColorColumn );
282 
283  QColor newColor = mModel->data( colorIndex, Qt::DisplayRole ).value<QColor>();
284  newColor.setAlpha( newOpacity );
285  mModel->setData( colorIndex, newColor, Qt::EditRole );
286  }
287  }
288  connect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
289 
290  emit widgetChanged();
291  }
292 }
293 
294 void QgsPalettedRendererWidget::changeLabel()
295 {
296  QItemSelection sel = mTreeView->selectionModel()->selection();
297 
298  QModelIndex labelIndex = mModel->index( sel.first().top(), QgsPalettedRendererModel::LabelColumn );
299  QString currentLabel = mModel->data( labelIndex, Qt::DisplayRole ).toString();
300 
301  bool ok;
302  QString newLabel = QInputDialog::getText( this, tr( "Label" ), tr( "Change label" ), QLineEdit::Normal, currentLabel, &ok );
303  if ( ok )
304  {
305  // don't want to emit widgetChanged multiple times
306  disconnect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
307 
308  const auto constSel = sel;
309  for ( const QItemSelectionRange &range : constSel )
310  {
311  const auto constIndexes = range.indexes();
312  for ( const QModelIndex &index : constIndexes )
313  {
314  labelIndex = mModel->index( index.row(), QgsPalettedRendererModel::LabelColumn );
315  mModel->setData( labelIndex, newLabel, Qt::EditRole );
316  }
317  }
318  connect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
319 
320  emit widgetChanged();
321  }
322 }
323 
324 void QgsPalettedRendererWidget::applyColorRamp()
325 {
326  std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
327  if ( !ramp )
328  {
329  return;
330  }
331 
332  disconnect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
333 
334  QgsPalettedRasterRenderer::ClassData data = mModel->classData();
335  QgsPalettedRasterRenderer::ClassData::iterator cIt = data.begin();
336 
337  double numberOfEntries = data.count();
338  int i = 0;
339 
340  if ( QgsRandomColorRamp *randomRamp = dynamic_cast<QgsRandomColorRamp *>( ramp.get() ) )
341  {
342  //ramp is a random colors ramp, so inform it of the total number of required colors
343  //this allows the ramp to pregenerate a set of visually distinctive colors
344  randomRamp->setTotalColorCount( numberOfEntries );
345  }
346 
347  if ( numberOfEntries > 1 )
348  numberOfEntries -= 1; //avoid duplicate first color
349 
350  for ( ; cIt != data.end(); ++cIt )
351  {
352  cIt->color = ramp->color( i / numberOfEntries );
353  i++;
354  }
355  mModel->setClassData( data );
356 
357  connect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
358  emit widgetChanged();
359 }
360 
361 void QgsPalettedRendererWidget::loadColorTable()
362 {
363  QgsSettings settings;
364  QString lastDir = settings.value( QStringLiteral( "lastColorMapDir" ), QDir::homePath() ).toString();
365  QString fileName = QFileDialog::getOpenFileName( this, tr( "Load Color Table from File" ), lastDir );
366  if ( !fileName.isEmpty() )
367  {
369  if ( !classes.isEmpty() )
370  {
371  disconnect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
372  mModel->setClassData( classes );
373  emit widgetChanged();
374  connect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
375  }
376  else
377  {
378  QMessageBox::critical( nullptr, tr( "Load Color Table" ), tr( "Could not interpret file as a raster color table." ) );
379  }
380  }
381 }
382 
383 void QgsPalettedRendererWidget::saveColorTable()
384 {
385  QgsSettings settings;
386  QString lastDir = settings.value( QStringLiteral( "lastColorMapDir" ), QDir::homePath() ).toString();
387  QString fileName = QFileDialog::getSaveFileName( this, tr( "Save Color Table as File" ), lastDir, tr( "Text (*.clr)" ) );
388  if ( !fileName.isEmpty() )
389  {
390  if ( !fileName.endsWith( QLatin1String( ".clr" ), Qt::CaseInsensitive ) )
391  {
392  fileName = fileName + ".clr";
393  }
394 
395  QFile outputFile( fileName );
396  if ( outputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
397  {
398  QTextStream outputStream( &outputFile );
399  outputStream << QgsPalettedRasterRenderer::classDataToString( mModel->classData() );
400  outputStream.flush();
401  outputFile.close();
402 
403  QFileInfo fileInfo( fileName );
404  settings.setValue( QStringLiteral( "lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
405  }
406  else
407  {
408  QMessageBox::warning( this, tr( "Save Color Table as File" ), tr( "Write access denied. Adjust the file permissions and try again.\n\n" ) );
409  }
410  }
411 }
412 
413 void QgsPalettedRendererWidget::classify()
414 {
415  if ( mRasterLayer )
416  {
418  if ( !provider )
419  {
420  return;
421  }
422 
423  if ( mGatherer )
424  {
425  mGatherer->stop();
426  return;
427  }
428 
429  mGatherer = new QgsPalettedRendererClassGatherer( mRasterLayer, mBandComboBox->currentBand(), mModel->classData(), btnColorRamp->colorRamp() );
430 
431  connect( mGatherer, &QgsPalettedRendererClassGatherer::progressChanged, mCalculatingProgressBar, [ = ]( int progress )
432  {
433  mCalculatingProgressBar->setValue( progress );
434  } );
435 
436  mCalculatingProgressBar->show();
437  mCancelButton->show();
438  connect( mCancelButton, &QPushButton::clicked, mGatherer, &QgsPalettedRendererClassGatherer::stop );
439 
440  connect( mGatherer, &QgsPalettedRendererClassGatherer::collectedClasses, this, &QgsPalettedRendererWidget::gatheredClasses );
441  connect( mGatherer, &QgsPalettedRendererClassGatherer::finished, this, &QgsPalettedRendererWidget::gathererThreadFinished );
442  mClassifyButton->setText( tr( "Calculating…" ) );
443  mClassifyButton->setEnabled( false );
444  mGatherer->start();
445  }
446 }
447 
448 void QgsPalettedRendererWidget::loadFromLayer()
449 {
450  //read default palette settings from layer
452  if ( provider )
453  {
454  QList<QgsColorRampShader::ColorRampItem> table = provider->colorTable( mBandComboBox->currentBand() );
455  if ( !table.isEmpty() )
456  {
457  QgsPalettedRasterRenderer::ClassData classes = QgsPalettedRasterRenderer::colorTableToClassData( provider->colorTable( mBandComboBox->currentBand() ) );
458  mModel->setClassData( classes );
459  emit widgetChanged();
460  }
461  }
462 }
463 
464 void QgsPalettedRendererWidget::bandChanged( int band )
465 {
466  if ( band == mBand )
467  return;
468 
469  bool deleteExisting = false;
470  if ( !mModel->classData().isEmpty() )
471  {
472  int res = QMessageBox::question( this,
473  tr( "Delete Classification" ),
474  tr( "The classification band was changed from %1 to %2.\n"
475  "Should the existing classes be deleted?" ).arg( mBand ).arg( band ),
476  QMessageBox::Yes | QMessageBox::No );
477 
478  deleteExisting = ( res == QMessageBox::Yes );
479  }
480 
481  mBand = band;
482  mModel->blockSignals( true );
483  if ( deleteExisting )
484  mModel->deleteAll();
485 
486  mModel->blockSignals( false );
487  emit widgetChanged();
488 }
489 
490 void QgsPalettedRendererWidget::gatheredClasses()
491 {
492  if ( !mGatherer || mGatherer->wasCanceled() )
493  return;
494 
495  mModel->setClassData( mGatherer->classes() );
496  emit widgetChanged();
497 }
498 
499 void QgsPalettedRendererWidget::gathererThreadFinished()
500 {
501  mGatherer->deleteLater();
502  mGatherer = nullptr;
503  mClassifyButton->setText( tr( "Classify" ) );
504  mClassifyButton->setEnabled( true );
505  mCalculatingProgressBar->hide();
506  mCancelButton->hide();
507 }
508 
509 void QgsPalettedRendererWidget::layerWillBeRemoved( QgsMapLayer *layer )
510 {
511  if ( mGatherer && mRasterLayer == layer )
512  {
513  mGatherer->stop();
514  mGatherer->wait();
515  }
516 }
517 
518 //
519 // QgsPalettedRendererModel
520 //
521 
523 QgsPalettedRendererModel::QgsPalettedRendererModel( QObject *parent )
524  : QAbstractItemModel( parent )
525 {
526 
527 }
528 
529 void QgsPalettedRendererModel::setClassData( const QgsPalettedRasterRenderer::ClassData &data )
530 {
531  beginResetModel();
532  mData = data;
533  endResetModel();
534 }
535 
536 QModelIndex QgsPalettedRendererModel::index( int row, int column, const QModelIndex &parent ) const
537 {
538  if ( column < 0 || column >= columnCount() )
539  {
540  //column out of bounds
541  return QModelIndex();
542  }
543 
544  if ( !parent.isValid() && row >= 0 && row < mData.size() )
545  {
546  //return an index for the item at this position
547  return createIndex( row, column );
548  }
549 
550  //only top level supported
551  return QModelIndex();
552 }
553 
554 QModelIndex QgsPalettedRendererModel::parent( const QModelIndex &index ) const
555 {
556  Q_UNUSED( index )
557 
558  //all items are top level
559  return QModelIndex();
560 }
561 
562 int QgsPalettedRendererModel::columnCount( const QModelIndex &parent ) const
563 {
564  if ( parent.isValid() )
565  return 0;
566 
567  return 3;
568 }
569 
570 int QgsPalettedRendererModel::rowCount( const QModelIndex &parent ) const
571 {
572  if ( parent.isValid() )
573  return 0;
574 
575  return mData.count();
576 }
577 
578 QVariant QgsPalettedRendererModel::data( const QModelIndex &index, int role ) const
579 {
580  if ( !index.isValid() )
581  return QVariant();
582 
583  switch ( role )
584  {
585  case Qt::DisplayRole:
586  case Qt::EditRole:
587  {
588  switch ( index.column() )
589  {
590  case ValueColumn:
591  return mData.at( index.row() ).value;
592 
593  case ColorColumn:
594  return mData.at( index.row() ).color;
595 
596  case LabelColumn:
597  return mData.at( index.row() ).label;
598  }
599  }
600 
601  default:
602  break;
603  }
604 
605  return QVariant();
606 }
607 
608 QVariant QgsPalettedRendererModel::headerData( int section, Qt::Orientation orientation, int role ) const
609 {
610  switch ( orientation )
611  {
612  case Qt::Vertical:
613  return QVariant();
614 
615  case Qt::Horizontal:
616  {
617  switch ( role )
618  {
619  case Qt::DisplayRole:
620  {
621  switch ( section )
622  {
623  case ValueColumn:
624  return tr( "Value" );
625 
626  case ColorColumn:
627  return tr( "Color" );
628 
629  case LabelColumn:
630  return tr( "Label" );
631  }
632  }
633 
634  }
635  break;
636  }
637 
638  default:
639  return QAbstractItemModel::headerData( section, orientation, role );
640  }
641  return QAbstractItemModel::headerData( section, orientation, role );
642 }
643 
644 bool QgsPalettedRendererModel::setData( const QModelIndex &index, const QVariant &value, int )
645 {
646  if ( !index.isValid() )
647  return false;
648  if ( index.row() >= mData.length() )
649  return false;
650 
651  switch ( index.column() )
652  {
653  case ValueColumn:
654  {
655  bool ok = false;
656  int newValue = value.toInt( &ok );
657  if ( !ok )
658  return false;
659 
660  mData[ index.row() ].value = newValue;
661  emit dataChanged( index, index );
662  emit classesChanged();
663  return true;
664  }
665 
666  case ColorColumn:
667  {
668  mData[ index.row() ].color = value.value<QColor>();
669  emit dataChanged( index, index );
670  emit classesChanged();
671  return true;
672  }
673 
674  case LabelColumn:
675  {
676  mData[ index.row() ].label = value.toString();
677  emit dataChanged( index, index );
678  emit classesChanged();
679  return true;
680  }
681  }
682 
683  return false;
684 }
685 
686 Qt::ItemFlags QgsPalettedRendererModel::flags( const QModelIndex &index ) const
687 {
688  if ( !index.isValid() )
689  return QAbstractItemModel::flags( index ) | Qt::ItemIsDropEnabled;
690 
691  Qt::ItemFlags f = QAbstractItemModel::flags( index );
692  switch ( index.column() )
693  {
694  case ValueColumn:
695  case LabelColumn:
696  case ColorColumn:
697  f = f | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
698  break;
699  }
700  return f | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
701 }
702 
703 bool QgsPalettedRendererModel::removeRows( int row, int count, const QModelIndex &parent )
704 {
705  if ( row < 0 || row >= mData.count() )
706  return false;
707  if ( parent.isValid() )
708  return false;
709 
710  for ( int i = row + count - 1; i >= row; --i )
711  {
712  beginRemoveRows( parent, i, i );
713  mData.removeAt( i );
714  endRemoveRows();
715  }
716  emit classesChanged();
717  return true;
718 }
719 
720 bool QgsPalettedRendererModel::insertRows( int row, int count, const QModelIndex & )
721 {
722  QgsPalettedRasterRenderer::ClassData::const_iterator cIt = mData.constBegin();
723  int currentMaxValue = -std::numeric_limits<int>::max();
724  for ( ; cIt != mData.constEnd(); ++cIt )
725  {
726  int value = cIt->value;
727  currentMaxValue = std::max( value, currentMaxValue );
728  }
729  int nextValue = std::max( 0, currentMaxValue + 1 );
730 
731  beginInsertRows( QModelIndex(), row, row + count - 1 );
732  for ( int i = row; i < row + count; ++i, ++nextValue )
733  {
734  mData.insert( i, QgsPalettedRasterRenderer::Class( nextValue, QColor( 200, 200, 200 ), QLocale().toString( nextValue ) ) );
735  }
736  endInsertRows();
737  emit classesChanged();
738  return true;
739 }
740 
741 Qt::DropActions QgsPalettedRendererModel::supportedDropActions() const
742 {
743  return Qt::MoveAction;
744 }
745 
746 QStringList QgsPalettedRendererModel::mimeTypes() const
747 {
748  QStringList types;
749  types << QStringLiteral( "application/x-qgspalettedrenderermodel" );
750  return types;
751 }
752 
753 QMimeData *QgsPalettedRendererModel::mimeData( const QModelIndexList &indexes ) const
754 {
755  QMimeData *mimeData = new QMimeData();
756  QByteArray encodedData;
757 
758  QDataStream stream( &encodedData, QIODevice::WriteOnly );
759 
760  // Create list of rows
761  const auto constIndexes = indexes;
762  for ( const QModelIndex &index : constIndexes )
763  {
764  if ( !index.isValid() || index.column() != 0 )
765  continue;
766 
767  stream << index.row();
768  }
769  mimeData->setData( QStringLiteral( "application/x-qgspalettedrenderermodel" ), encodedData );
770  return mimeData;
771 }
772 
773 bool QgsPalettedRendererModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex & )
774 {
775  Q_UNUSED( column )
776  if ( action != Qt::MoveAction ) return true;
777 
778  if ( !data->hasFormat( QStringLiteral( "application/x-qgspalettedrenderermodel" ) ) )
779  return false;
780 
781  QByteArray encodedData = data->data( QStringLiteral( "application/x-qgspalettedrenderermodel" ) );
782  QDataStream stream( &encodedData, QIODevice::ReadOnly );
783 
784  QVector<int> rows;
785  while ( !stream.atEnd() )
786  {
787  int r;
788  stream >> r;
789  rows.append( r );
790  }
791 
793  for ( int i = 0; i < rows.count(); ++i )
794  newData << mData.at( rows.at( i ) );
795 
796  if ( row < 0 )
797  row = mData.count();
798 
799  beginInsertRows( QModelIndex(), row, row + rows.count() - 1 );
800  for ( int i = 0; i < rows.count(); ++i )
801  mData.insert( row + i, newData.at( i ) );
802  endInsertRows();
803  emit classesChanged();
804  return true;
805 }
806 
807 QModelIndex QgsPalettedRendererModel::addEntry( const QColor &color )
808 {
809  insertRow( rowCount() );
810  QModelIndex newRow = index( mData.count() - 1, 1 );
811  setData( newRow, color );
812  return newRow;
813 }
814 
815 void QgsPalettedRendererModel::deleteAll()
816 {
817  beginResetModel();
818  mData.clear();
819  endResetModel();
820  emit classesChanged();
821 }
822 
823 void QgsPalettedRendererClassGatherer::run()
824 {
825  mWasCanceled = false;
826 
827  // allow responsive cancellation
828  mFeedback = new QgsRasterBlockFeedback();
829  connect( mFeedback, &QgsRasterBlockFeedback::progressChanged, this, &QgsPalettedRendererClassGatherer::progressChanged );
830 
831  QgsPalettedRasterRenderer::ClassData newClasses = QgsPalettedRasterRenderer::classDataFromRaster( mLayer->dataProvider(), mBandNumber, mRamp.get(), mFeedback );
832 
833  // combine existing classes with new classes
834  QgsPalettedRasterRenderer::ClassData::iterator classIt = newClasses.begin();
835  emit progressChanged( 0 );
836  qlonglong i = 0;
837  for ( ; classIt != newClasses.end(); ++classIt )
838  {
839  // check if existing classes contains this same class
840  for ( const QgsPalettedRasterRenderer::Class &existingClass : qgis::as_const( mClasses ) )
841  {
842  if ( existingClass.value == classIt->value )
843  {
844  classIt->color = existingClass.color;
845  classIt->label = existingClass.label;
846  break;
847  }
848  }
849  i ++;
850  emit progressChanged( 100 * ( i / static_cast<float>( newClasses.count() ) ) );
851  }
852  mClasses = newClasses;
853 
854  // be overly cautious - it's *possible* stop() might be called between deleting mFeedback and nulling it
855  mFeedbackMutex.lock();
856  delete mFeedback;
857  mFeedback = nullptr;
858  mFeedbackMutex.unlock();
859 
860  emit collectedClasses();
861 }
862 
QgsCompoundColorWidget::setAllowOpacity
void setAllowOpacity(bool allowOpacity)
Sets whether opacity modification (transparency) is permitted for the color dialog.
Definition: qgscompoundcolorwidget.cpp:307
QgsProject::layerWillBeRemoved
void layerWillBeRemoved(const QString &layerId)
Emitted when a layer is about to be removed from the registry.
QgsPalettedRasterRenderer::ClassData
QList< QgsPalettedRasterRenderer::Class > ClassData
Map of value to class properties.
Definition: qgspalettedrasterrenderer.h:59
QgsRasterBandComboBox::bandChanged
void bandChanged(int band)
Emitted when the currently selected band changes.
qgspalettedrendererwidget.h
qgsrasterlayer.h
QgsPalettedRasterRenderer::sourceColorRamp
QgsColorRamp * sourceColorRamp() const
Gets the source color ramp.
Definition: qgspalettedrasterrenderer.cpp:345
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:174
QgsRasterRendererWidget::mRasterLayer
QgsRasterLayer * mRasterLayer
Definition: qgsrasterrendererwidget.h:92
QgsColorSwatchDelegate
A delegate for showing a color swatch in a list.
Definition: qgscolorschemelist.h:37
QgsPanelWidget::findParentPanel
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
Definition: qgspanelwidget.cpp:49
QgsPalettedRendererWidget::QgsPalettedRendererWidget
QgsPalettedRendererWidget(QgsRasterLayer *layer, const QgsRectangle &extent=QgsRectangle())
Definition: qgspalettedrendererwidget.cpp:36
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:468
QgsPalettedRendererWidget::renderer
QgsRasterRenderer * renderer() override
Definition: qgspalettedrendererwidget.cpp:133
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QgsPalettedRendererWidget::setFromRenderer
void setFromRenderer(const QgsRasterRenderer *r)
Definition: qgspalettedrendererwidget.cpp:146
QgsPalettedRasterRenderer::colorTableToClassData
static QgsPalettedRasterRenderer::ClassData colorTableToClassData(const QList< QgsColorRampShader::ColorRampItem > &table)
Converts a raster color table to paletted renderer class data.
Definition: qgspalettedrasterrenderer.cpp:350
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QgsProject
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:95
QgsRandomColorRamp
Totally random color ramp.
Definition: qgscolorramp.h:455
QgsCompoundColorWidget
A custom QGIS widget for selecting a color, including options for selecting colors via hue wheel,...
Definition: qgscompoundcolorwidget.h:34
QgsPalettedRasterRenderer::setSourceColorRamp
void setSourceColorRamp(QgsColorRamp *ramp)
Set the source color ramp.
Definition: qgspalettedrasterrenderer.cpp:340
QgsCompoundColorWidget::LayoutVertical
@ LayoutVertical
Use a narrower, vertically stacked layout.
Definition: qgscompoundcolorwidget.h:44
QgsCompoundColorWidget::currentColorChanged
void currentColorChanged(const QColor &color)
Emitted when the dialog's color changes.
QgsPanelWidget
Base class for any widget that can be shown as a inline panel.
Definition: qgspanelwidget.h:30
whileBlocking
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:262
qgspalettedrasterrenderer.h
QgsRasterRenderer
Raster renderer pipe that applies colors to a raster.
Definition: qgsrasterrenderer.h:39
QgsPalettedRasterRenderer::classDataFromRaster
static QgsPalettedRasterRenderer::ClassData classDataFromRaster(QgsRasterInterface *raster, int bandNumber, QgsColorRamp *ramp=nullptr, QgsRasterBlockFeedback *feedback=nullptr)
Generates class data from a raster, for the specified bandNumber.
Definition: qgspalettedrasterrenderer.cpp:474
QgsPalettedRasterRenderer::band
int band() const
Returns the raster band used for rendering the raster.
Definition: qgspalettedrasterrenderer.h:100
QgsPalettedRasterRenderer::Class
Properties of a single value class.
Definition: qgspalettedrasterrenderer.h:41
QgsRasterLayer::dataProvider
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
Definition: qgsrasterlayer.cpp:234
Qgis::UI_SCALE_FACTOR
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:182
QgsFeedback::progressChanged
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
QgsPalettedRasterRenderer::classDataToString
static QString classDataToString(const QgsPalettedRasterRenderer::ClassData &classes)
Converts classes to a string representation, using the .clr/gdal color table file format.
Definition: qgspalettedrasterrenderer.cpp:455
QgsPalettedRendererWidget::~QgsPalettedRendererWidget
~QgsPalettedRendererWidget() override
Definition: qgspalettedrendererwidget.cpp:124
QgsSettings::setValue
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Definition: qgssettings.cpp:289
QgsRasterDataProvider::colorTable
virtual QList< QgsColorRampShader::ColorRampItem > colorTable(int bandNo) const
Definition: qgsrasterdataprovider.h:262
QgsPalettedRasterRenderer
Renderer for paletted raster images.
Definition: qgspalettedrasterrenderer.h:36
QgsRasterLayer
Represents a raster layer.
Definition: qgsrasterlayer.h:71
QgsPanelWidget::setPanelTitle
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
Definition: qgspanelwidget.h:44
qgscolordialog.h
QgsMapLayer
Base class for all map layer types.
Definition: qgsmaplayer.h:83
QgsRasterBlockFeedback
Feedback object tailored for raster block reading.
Definition: qgsrasterinterface.h:41
qgssettings.h
QgsRasterLayer::renderer
QgsRasterRenderer * renderer() const
Returns the raster's renderer.
Definition: qgsrasterlayer.h:246
QgsPalettedRasterRenderer::classes
ClassData classes() const
Returns a map of value to classes (colors) used by the renderer.
Definition: qgspalettedrasterrenderer.cpp:99
QgsRasterRendererWidget
Definition: qgsrasterrendererwidget.h:37
QgsRasterRendererWidget::widgetChanged
void widgetChanged()
Emitted when something on the widget has changed.
QgsPalettedRasterRenderer::classDataFromFile
static QgsPalettedRasterRenderer::ClassData classDataFromFile(const QString &path)
Opens a color table file and returns corresponding paletted renderer class data.
Definition: qgspalettedrasterrenderer.cpp:442
QgsRasterDataProvider
Base class for raster data providers.
Definition: qgsrasterdataprovider.h:89
qgsproject.h
QgsColorDialog::getColor
static QColor getColor(const QColor &initialColor, QWidget *parent, const QString &title=QString(), bool allowOpacity=false)
Returns a color selection from a color dialog.
Definition: qgscolordialog.cpp:82
qgsrasterdataprovider.h
QgsColorRampButton::colorRampChanged
void colorRampChanged()
Emitted whenever a new color ramp is set for the button.