QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 cancellation
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:40
Base class for all map layer types.
Definition: qgsmaplayer.h:63
Renderer for paletted raster images.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:152
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
void colorRampChanged()
Emitted whenever a new color ramp is set for the button.
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
Properties of a single value class.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Base class for any widget that can be shown as a inline panel.
QgsRasterDataProvider * dataProvider() override
Returns the layer&#39;s data provider.
void setFromRenderer(const QgsRasterRenderer *r)
int band() const
Returns the raster band used for rendering the raster.
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
QgsRasterRenderer * renderer() const
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget...
ClassData classes() const
Returns a map of value to classes (colors) used by the renderer.
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
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:225
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:411
QgsColorRamp * sourceColorRamp() const
Gets the source color ramp.
virtual QList< QgsColorRampShader::ColorRampItem > colorTable(int bandNo) const
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.