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