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