QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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, &QProgressBar::setValue );
432  mCalculatingProgressBar->show();
433  mCancelButton->show();
434  connect( mCancelButton, &QPushButton::clicked, mGatherer, &QgsPalettedRendererClassGatherer::stop );
435 
436  connect( mGatherer, &QgsPalettedRendererClassGatherer::collectedClasses, this, &QgsPalettedRendererWidget::gatheredClasses );
437  connect( mGatherer, &QgsPalettedRendererClassGatherer::finished, this, &QgsPalettedRendererWidget::gathererThreadFinished );
438  mClassifyButton->setText( tr( "Calculating…" ) );
439  mClassifyButton->setEnabled( false );
440  mGatherer->start();
441  }
442 }
443 
444 void QgsPalettedRendererWidget::loadFromLayer()
445 {
446  //read default palette settings from layer
448  if ( provider )
449  {
450  QList<QgsColorRampShader::ColorRampItem> table = provider->colorTable( mBandComboBox->currentBand() );
451  if ( !table.isEmpty() )
452  {
453  QgsPalettedRasterRenderer::ClassData classes = QgsPalettedRasterRenderer::colorTableToClassData( provider->colorTable( mBandComboBox->currentBand() ) );
454  mModel->setClassData( classes );
455  emit widgetChanged();
456  }
457  }
458 }
459 
460 void QgsPalettedRendererWidget::bandChanged( int band )
461 {
462  if ( band == mBand )
463  return;
464 
465  bool deleteExisting = false;
466  if ( !mModel->classData().isEmpty() )
467  {
468  int res = QMessageBox::question( this,
469  tr( "Delete Classification" ),
470  tr( "The classification band was changed from %1 to %2.\n"
471  "Should the existing classes be deleted?" ).arg( mBand ).arg( band ),
472  QMessageBox::Yes | QMessageBox::No );
473 
474  deleteExisting = ( res == QMessageBox::Yes );
475  }
476 
477  mBand = band;
478  mModel->blockSignals( true );
479  if ( deleteExisting )
480  mModel->deleteAll();
481 
482  mModel->blockSignals( false );
483  emit widgetChanged();
484 }
485 
486 void QgsPalettedRendererWidget::gatheredClasses()
487 {
488  if ( !mGatherer || mGatherer->wasCanceled() )
489  return;
490 
491  mModel->setClassData( mGatherer->classes() );
492  emit widgetChanged();
493 }
494 
495 void QgsPalettedRendererWidget::gathererThreadFinished()
496 {
497  mGatherer->deleteLater();
498  mGatherer = nullptr;
499  mClassifyButton->setText( tr( "Classify" ) );
500  mClassifyButton->setEnabled( true );
501  mCalculatingProgressBar->hide();
502  mCancelButton->hide();
503 }
504 
505 void QgsPalettedRendererWidget::layerWillBeRemoved( QgsMapLayer *layer )
506 {
507  if ( mGatherer && mRasterLayer == layer )
508  {
509  mGatherer->stop();
510  mGatherer->wait();
511  }
512 }
513 
514 //
515 // QgsPalettedRendererModel
516 //
517 
519 QgsPalettedRendererModel::QgsPalettedRendererModel( QObject *parent )
520  : QAbstractItemModel( parent )
521 {
522 
523 }
524 
525 void QgsPalettedRendererModel::setClassData( const QgsPalettedRasterRenderer::ClassData &data )
526 {
527  beginResetModel();
528  mData = data;
529  endResetModel();
530 }
531 
532 QModelIndex QgsPalettedRendererModel::index( int row, int column, const QModelIndex &parent ) const
533 {
534  if ( column < 0 || column >= columnCount() )
535  {
536  //column out of bounds
537  return QModelIndex();
538  }
539 
540  if ( !parent.isValid() && row >= 0 && row < mData.size() )
541  {
542  //return an index for the item at this position
543  return createIndex( row, column );
544  }
545 
546  //only top level supported
547  return QModelIndex();
548 }
549 
550 QModelIndex QgsPalettedRendererModel::parent( const QModelIndex &index ) const
551 {
552  Q_UNUSED( index )
553 
554  //all items are top level
555  return QModelIndex();
556 }
557 
558 int QgsPalettedRendererModel::columnCount( const QModelIndex &parent ) const
559 {
560  if ( parent.isValid() )
561  return 0;
562 
563  return 3;
564 }
565 
566 int QgsPalettedRendererModel::rowCount( const QModelIndex &parent ) const
567 {
568  if ( parent.isValid() )
569  return 0;
570 
571  return mData.count();
572 }
573 
574 QVariant QgsPalettedRendererModel::data( const QModelIndex &index, int role ) const
575 {
576  if ( !index.isValid() )
577  return QVariant();
578 
579  switch ( role )
580  {
581  case Qt::DisplayRole:
582  case Qt::EditRole:
583  {
584  switch ( index.column() )
585  {
586  case ValueColumn:
587  return mData.at( index.row() ).value;
588 
589  case ColorColumn:
590  return mData.at( index.row() ).color;
591 
592  case LabelColumn:
593  return mData.at( index.row() ).label;
594  }
595  }
596 
597  default:
598  break;
599  }
600 
601  return QVariant();
602 }
603 
604 QVariant QgsPalettedRendererModel::headerData( int section, Qt::Orientation orientation, int role ) const
605 {
606  switch ( orientation )
607  {
608  case Qt::Vertical:
609  return QVariant();
610 
611  case Qt::Horizontal:
612  {
613  switch ( role )
614  {
615  case Qt::DisplayRole:
616  {
617  switch ( section )
618  {
619  case ValueColumn:
620  return tr( "Value" );
621 
622  case ColorColumn:
623  return tr( "Color" );
624 
625  case LabelColumn:
626  return tr( "Label" );
627  }
628  }
629 
630  }
631  break;
632  }
633 
634  default:
635  return QAbstractItemModel::headerData( section, orientation, role );
636  }
637  return QAbstractItemModel::headerData( section, orientation, role );
638 }
639 
640 bool QgsPalettedRendererModel::setData( const QModelIndex &index, const QVariant &value, int )
641 {
642  if ( !index.isValid() )
643  return false;
644  if ( index.row() >= mData.length() )
645  return false;
646 
647  switch ( index.column() )
648  {
649  case ValueColumn:
650  {
651  bool ok = false;
652  int newValue = value.toInt( &ok );
653  if ( !ok )
654  return false;
655 
656  mData[ index.row() ].value = newValue;
657  emit dataChanged( index, index );
658  emit classesChanged();
659  return true;
660  }
661 
662  case ColorColumn:
663  {
664  mData[ index.row() ].color = value.value<QColor>();
665  emit dataChanged( index, index );
666  emit classesChanged();
667  return true;
668  }
669 
670  case LabelColumn:
671  {
672  mData[ index.row() ].label = value.toString();
673  emit dataChanged( index, index );
674  emit classesChanged();
675  return true;
676  }
677  }
678 
679  return false;
680 }
681 
682 Qt::ItemFlags QgsPalettedRendererModel::flags( const QModelIndex &index ) const
683 {
684  if ( !index.isValid() )
685  return QAbstractItemModel::flags( index ) | Qt::ItemIsDropEnabled;
686 
687  Qt::ItemFlags f = QAbstractItemModel::flags( index );
688  switch ( index.column() )
689  {
690  case ValueColumn:
691  case LabelColumn:
692  case ColorColumn:
693  f = f | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
694  break;
695  }
696  return f | Qt::ItemIsEnabled | Qt::ItemIsSelectable;;
697 }
698 
699 bool QgsPalettedRendererModel::removeRows( int row, int count, const QModelIndex &parent )
700 {
701  if ( row < 0 || row >= mData.count() )
702  return false;
703  if ( parent.isValid() )
704  return false;
705 
706  for ( int i = row + count - 1; i >= row; --i )
707  {
708  beginRemoveRows( parent, i, i );
709  mData.removeAt( i );
710  endRemoveRows();
711  }
712  emit classesChanged();
713  return true;
714 }
715 
716 bool QgsPalettedRendererModel::insertRows( int row, int count, const QModelIndex & )
717 {
718  QgsPalettedRasterRenderer::ClassData::const_iterator cIt = mData.constBegin();
719  int currentMaxValue = -std::numeric_limits<int>::max();
720  for ( ; cIt != mData.constEnd(); ++cIt )
721  {
722  int value = cIt->value;
723  currentMaxValue = std::max( value, currentMaxValue );
724  }
725  int nextValue = std::max( 0, currentMaxValue + 1 );
726 
727  beginInsertRows( QModelIndex(), row, row + count - 1 );
728  for ( int i = row; i < row + count; ++i, ++nextValue )
729  {
730  mData.insert( i, QgsPalettedRasterRenderer::Class( nextValue, QColor( 200, 200, 200 ), QString::number( nextValue ) ) );
731  }
732  endInsertRows();
733  emit classesChanged();
734  return true;
735 }
736 
737 Qt::DropActions QgsPalettedRendererModel::supportedDropActions() const
738 {
739  return Qt::MoveAction;
740 }
741 
742 QStringList QgsPalettedRendererModel::mimeTypes() const
743 {
744  QStringList types;
745  types << QStringLiteral( "application/x-qgspalettedrenderermodel" );
746  return types;
747 }
748 
749 QMimeData *QgsPalettedRendererModel::mimeData( const QModelIndexList &indexes ) const
750 {
751  QMimeData *mimeData = new QMimeData();
752  QByteArray encodedData;
753 
754  QDataStream stream( &encodedData, QIODevice::WriteOnly );
755 
756  // Create list of rows
757  const auto constIndexes = indexes;
758  for ( const QModelIndex &index : constIndexes )
759  {
760  if ( !index.isValid() || index.column() != 0 )
761  continue;
762 
763  stream << index.row();
764  }
765  mimeData->setData( QStringLiteral( "application/x-qgspalettedrenderermodel" ), encodedData );
766  return mimeData;
767 }
768 
769 bool QgsPalettedRendererModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex & )
770 {
771  Q_UNUSED( column )
772  if ( action != Qt::MoveAction ) return true;
773 
774  if ( !data->hasFormat( QStringLiteral( "application/x-qgspalettedrenderermodel" ) ) )
775  return false;
776 
777  QByteArray encodedData = data->data( QStringLiteral( "application/x-qgspalettedrenderermodel" ) );
778  QDataStream stream( &encodedData, QIODevice::ReadOnly );
779 
780  QVector<int> rows;
781  while ( !stream.atEnd() )
782  {
783  int r;
784  stream >> r;
785  rows.append( r );
786  }
787 
789  for ( int i = 0; i < rows.count(); ++i )
790  newData << mData.at( rows.at( i ) );
791 
792  if ( row < 0 )
793  row = mData.count();
794 
795  beginInsertRows( QModelIndex(), row, row + rows.count() - 1 );
796  for ( int i = 0; i < rows.count(); ++i )
797  mData.insert( row + i, newData.at( i ) );
798  endInsertRows();
799  emit classesChanged();
800  return true;
801 }
802 
803 QModelIndex QgsPalettedRendererModel::addEntry( const QColor &color )
804 {
805  insertRow( rowCount() );
806  QModelIndex newRow = index( mData.count() - 1, 1 );
807  setData( newRow, color );
808  return newRow;
809 }
810 
811 void QgsPalettedRendererModel::deleteAll()
812 {
813  beginResetModel();
814  mData.clear();
815  endResetModel();
816  emit classesChanged();
817 }
818 
819 void QgsPalettedRendererClassGatherer::run()
820 {
821  mWasCanceled = false;
822 
823  // allow responsive cancellation
824  mFeedback = new QgsRasterBlockFeedback();
825  connect( mFeedback, &QgsRasterBlockFeedback::progressChanged, this, &QgsPalettedRendererClassGatherer::progressChanged );
826 
827  QgsPalettedRasterRenderer::ClassData newClasses = QgsPalettedRasterRenderer::classDataFromRaster( mLayer->dataProvider(), mBandNumber, mRamp.get(), mFeedback );
828 
829  // combine existing classes with new classes
830  QgsPalettedRasterRenderer::ClassData::iterator classIt = newClasses.begin();
831  for ( ; classIt != newClasses.end(); ++classIt )
832  {
833  // check if existing classes contains this same class
834  for ( const QgsPalettedRasterRenderer::Class &existingClass : qgis::as_const( mClasses ) )
835  {
836  if ( existingClass.value == classIt->value )
837  {
838  classIt->color = existingClass.color;
839  classIt->label = existingClass.label;
840  break;
841  }
842  }
843  }
844  mClasses = newClasses;
845 
846  // be overly cautious - it's *possible* stop() might be called between deleting mFeedback and nulling it
847  mFeedbackMutex.lock();
848  delete mFeedback;
849  mFeedback = nullptr;
850  mFeedbackMutex.unlock();
851 
852  emit collectedClasses();
853 }
854 
QgsCompoundColorWidget::setAllowOpacity
void setAllowOpacity(bool allowOpacity)
Sets whether opacity modification (transparency) is permitted for the color dialog.
Definition: qgscompoundcolorwidget.cpp:309
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:341
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
Definition: qgscolorschemelist.h:36
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:458
QgsPalettedRendererWidget::renderer
QgsRasterRenderer * renderer() override
Definition: qgspalettedrendererwidget.cpp:133
QgsSettings
Definition: qgssettings.h:61
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:346
QgsRectangle
Definition: qgsrectangle.h:41
QgsProject
Definition: qgsproject.h:92
QgsRandomColorRamp
Totally random color ramp. Returns colors generated at random, but constrained to some hardcoded satu...
Definition: qgscolorramp.h:427
QgsCompoundColorWidget
Definition: qgscompoundcolorwidget.h:33
QgsPalettedRasterRenderer::setSourceColorRamp
void setSourceColorRamp(QgsColorRamp *ramp)
Set the source color ramp.
Definition: qgspalettedrasterrenderer.cpp:336
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:29
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
Definition: qgsrasterrenderer.h:38
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:470
QgsPalettedRasterRenderer::band
int band() const
Returns the raster band used for rendering the raster.
Definition: qgspalettedrasterrenderer.h:98
QgsPalettedRasterRenderer::Class
Properties of a single value class.
Definition: qgspalettedrasterrenderer.h:40
Qgis::UI_SCALE_FACTOR
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:195
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:451
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:259
QgsPalettedRasterRenderer
Definition: qgspalettedrasterrenderer.h:35
QgsRasterLayer
Definition: qgsrasterlayer.h:72
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
Definition: qgsmaplayer.h:81
QgsRasterBlockFeedback
Definition: qgsrasterinterface.h:40
qgssettings.h
QgsRasterLayer::renderer
QgsRasterRenderer * renderer() const
Returns the raster's renderer.
Definition: qgsrasterlayer.h:247
QgsPalettedRasterRenderer::classes
ClassData classes() const
Returns a map of value to classes (colors) used by the renderer.
Definition: qgspalettedrasterrenderer.cpp:94
QgsRasterRendererWidget
Definition: qgsrasterrendererwidget.h:36
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:438
QgsRasterDataProvider
Definition: qgsrasterdataprovider.h:88
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
QgsRasterLayer::dataProvider
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
Definition: qgsrasterlayer.cpp:233
qgsrasterdataprovider.h
QgsColorRampButton::colorRampChanged
void colorRampChanged()
Emitted whenever a new color ramp is set for the button.