QGIS API Documentation  3.12.1-București (121cc00ff0)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 
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:182
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.
Represents a raster layer.
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:91
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:262
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:450
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.