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