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