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