QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
qgscompoundcolorwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscompoundcolorwidget.cpp
3 --------------------------
4 begin : April 2016
5 copyright : (C) 2016 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "moc_qgscompoundcolorwidget.cpp"
18#include "qgscolorscheme.h"
20#include "qgssymbollayerutils.h"
21#include "qgsapplication.h"
22#include "qgssettings.h"
23#include "qgsscreenhelper.h"
24#include "qgsguiutils.h"
25
26#include <QButtonGroup>
27#include <QHeaderView>
28#include <QPushButton>
29#include <QMenu>
30#include <QToolButton>
31#include <QFileDialog>
32#include <QMessageBox>
33#include <QMouseEvent>
34#include <QScreen>
35#include <QInputDialog>
36#include <QVBoxLayout>
37#include <QRegularExpression>
38
39QgsCompoundColorWidget::QgsCompoundColorWidget( QWidget *parent, const QColor &color, Layout widgetLayout )
40 : QgsPanelWidget( parent )
41{
42 setupUi( this );
43
44 mScreenHelper = new QgsScreenHelper( this );
45
46 mRgbRadios = {
53 };
54
55 mCmykRadios = {
60 };
61
62 mRgbGroup = new QButtonGroup( this );
63 int i = 0;
64 for ( auto colorRadio : mRgbRadios )
65 mRgbGroup->addButton( colorRadio.first, i++ );
66
67 mCmykGroup = new QButtonGroup( this );
68 i = 0;
69 for ( auto colorRadio : mCmykRadios )
70 mCmykGroup->addButton( colorRadio.first, i++ );
71
72 connect( mRgbGroup, &QButtonGroup::idToggled, this, &QgsCompoundColorWidget::onColorButtonGroupToggled );
73 connect( mCmykGroup, &QButtonGroup::idToggled, this, &QgsCompoundColorWidget::onColorButtonGroupToggled );
74 connect( mAddColorToSchemeButton, &QPushButton::clicked, this, &QgsCompoundColorWidget::mAddColorToSchemeButton_clicked );
75 connect( mAddCustomColorButton, &QPushButton::clicked, this, &QgsCompoundColorWidget::mAddCustomColorButton_clicked );
76 connect( mSampleButton, &QPushButton::clicked, this, &QgsCompoundColorWidget::mSampleButton_clicked );
77 connect( mTabWidget, &QTabWidget::currentChanged, this, &QgsCompoundColorWidget::mTabWidget_currentChanged );
78 connect( mActionShowInButtons, &QAction::toggled, this, &QgsCompoundColorWidget::mActionShowInButtons_toggled );
79
80 mColorModel->addItem( tr( "RGB" ), QColor::Spec::Rgb );
81 mColorModel->addItem( tr( "CMYK" ), QColor::Spec::Cmyk );
82 connect( mColorModel, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [this]( int ) {
83 const QColor::Spec spec = static_cast<QColor::Spec>( mColorModel->currentData().toInt() );
84 if ( spec == QColor::Spec::Cmyk )
85 setColor( this->color().toCmyk() );
86 else
87 setColor( this->color().toRgb() );
88
89 updateComponent();
90 } );
91
92 if ( widgetLayout == LayoutVertical )
93 {
94 // shuffle stuff around
95 QVBoxLayout *newLayout = new QVBoxLayout();
96 newLayout->setContentsMargins( 0, 0, 0, 0 );
97 newLayout->addWidget( mTabWidget );
98 newLayout->addWidget( mSlidersWidget );
99 newLayout->addWidget( mPreviewWidget );
100 newLayout->addWidget( mSwatchesWidget );
101 delete layout();
102 setLayout( newLayout );
103 }
104
105 const QgsSettings settings;
106
107 mSchemeList->header()->hide();
108 mSchemeList->setColumnWidth( 0, static_cast<int>( Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 6 ) );
109
110 //get schemes with ShowInColorDialog set
111 refreshSchemeComboBox();
112 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
113
114 //choose a reasonable starting scheme
115 int activeScheme = settings.value( QStringLiteral( "Windows/ColorDialog/activeScheme" ), 0 ).toInt();
116 activeScheme = activeScheme >= mSchemeComboBox->count() ? 0 : activeScheme;
117
118 mSchemeList->setScheme( schemeList.at( activeScheme ) );
119
120 mSchemeComboBox->setCurrentIndex( activeScheme );
121 updateActionsForCurrentScheme();
122
123 //listen out for selection changes in list, so we can enable/disable the copy colors option
124 connect( mSchemeList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsCompoundColorWidget::listSelectionChanged );
125 //copy action defaults to disabled
126 mActionCopyColors->setEnabled( false );
127
128 connect( mActionCopyColors, &QAction::triggered, mSchemeList, &QgsColorSchemeList::copyColors );
129 connect( mActionPasteColors, &QAction::triggered, mSchemeList, &QgsColorSchemeList::pasteColors );
130 connect( mActionExportColors, &QAction::triggered, mSchemeList, &QgsColorSchemeList::showExportColorsDialog );
131 connect( mActionImportColors, &QAction::triggered, mSchemeList, &QgsColorSchemeList::showImportColorsDialog );
132 connect( mActionImportPalette, &QAction::triggered, this, &QgsCompoundColorWidget::importPalette );
133 connect( mActionRemovePalette, &QAction::triggered, this, &QgsCompoundColorWidget::removePalette );
134 connect( mActionNewPalette, &QAction::triggered, this, &QgsCompoundColorWidget::newPalette );
135 connect( mRemoveColorsFromSchemeButton, &QAbstractButton::clicked, mSchemeList, &QgsColorSchemeList::removeSelection );
136
137 QMenu *schemeMenu = new QMenu( mSchemeToolButton );
138 schemeMenu->addAction( mActionCopyColors );
139 schemeMenu->addAction( mActionPasteColors );
140 schemeMenu->addSeparator();
141 schemeMenu->addAction( mActionImportColors );
142 schemeMenu->addAction( mActionExportColors );
143 schemeMenu->addSeparator();
144 schemeMenu->addAction( mActionNewPalette );
145 schemeMenu->addAction( mActionImportPalette );
146 schemeMenu->addAction( mActionRemovePalette );
147 schemeMenu->addAction( mActionShowInButtons );
148 mSchemeToolButton->setMenu( schemeMenu );
149
150 connect( mSchemeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsCompoundColorWidget::schemeIndexChanged );
151 connect( mSchemeList, &QgsColorSchemeList::colorSelected, this, &QgsCompoundColorWidget::_setColor );
152
153 mOldColorLabel->hide();
154
155 mVerticalRamp->setOrientation( QgsColorRampWidget::Vertical );
156 mVerticalRamp->setInteriorMargin( 2 );
157 mVerticalRamp->setShowFrame( true );
158
159 mRedSlider->setComponent( QgsColorWidget::Red );
160 mGreenSlider->setComponent( QgsColorWidget::Green );
161 mBlueSlider->setComponent( QgsColorWidget::Blue );
162 mHueSlider->setComponent( QgsColorWidget::Hue );
163 mSaturationSlider->setComponent( QgsColorWidget::Saturation );
164 mValueSlider->setComponent( QgsColorWidget::Value );
165 mAlphaSlider->setComponent( QgsColorWidget::Alpha );
166 mCyanSlider->setComponent( QgsColorWidget::Cyan );
167 mMagentaSlider->setComponent( QgsColorWidget::Magenta );
168 mYellowSlider->setComponent( QgsColorWidget::Yellow );
169 mBlackSlider->setComponent( QgsColorWidget::Black );
170
171 mSwatchButton1->setShowMenu( false );
172 mSwatchButton1->setBehavior( QgsColorButton::SignalOnly );
173 mSwatchButton2->setShowMenu( false );
174 mSwatchButton2->setBehavior( QgsColorButton::SignalOnly );
175 mSwatchButton3->setShowMenu( false );
176 mSwatchButton3->setBehavior( QgsColorButton::SignalOnly );
177 mSwatchButton4->setShowMenu( false );
178 mSwatchButton4->setBehavior( QgsColorButton::SignalOnly );
179 mSwatchButton5->setShowMenu( false );
180 mSwatchButton5->setBehavior( QgsColorButton::SignalOnly );
181 mSwatchButton6->setShowMenu( false );
182 mSwatchButton6->setBehavior( QgsColorButton::SignalOnly );
183 mSwatchButton7->setShowMenu( false );
184 mSwatchButton7->setBehavior( QgsColorButton::SignalOnly );
185 mSwatchButton8->setShowMenu( false );
186 mSwatchButton8->setBehavior( QgsColorButton::SignalOnly );
187 mSwatchButton9->setShowMenu( false );
188 mSwatchButton9->setBehavior( QgsColorButton::SignalOnly );
189 mSwatchButton10->setShowMenu( false );
190 mSwatchButton10->setBehavior( QgsColorButton::SignalOnly );
191 mSwatchButton11->setShowMenu( false );
192 mSwatchButton11->setBehavior( QgsColorButton::SignalOnly );
193 mSwatchButton12->setShowMenu( false );
194 mSwatchButton12->setBehavior( QgsColorButton::SignalOnly );
195 mSwatchButton13->setShowMenu( false );
196 mSwatchButton13->setBehavior( QgsColorButton::SignalOnly );
197 mSwatchButton14->setShowMenu( false );
198 mSwatchButton14->setBehavior( QgsColorButton::SignalOnly );
199 mSwatchButton15->setShowMenu( false );
200 mSwatchButton15->setBehavior( QgsColorButton::SignalOnly );
201 mSwatchButton16->setShowMenu( false );
202 mSwatchButton16->setBehavior( QgsColorButton::SignalOnly );
203 //restore custom colors
204 mSwatchButton1->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor1" ), QVariant( QColor() ) ).value<QColor>() );
205 mSwatchButton2->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor2" ), QVariant( QColor() ) ).value<QColor>() );
206 mSwatchButton3->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor3" ), QVariant( QColor() ) ).value<QColor>() );
207 mSwatchButton4->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor4" ), QVariant( QColor() ) ).value<QColor>() );
208 mSwatchButton5->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor5" ), QVariant( QColor() ) ).value<QColor>() );
209 mSwatchButton6->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor6" ), QVariant( QColor() ) ).value<QColor>() );
210 mSwatchButton7->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor7" ), QVariant( QColor() ) ).value<QColor>() );
211 mSwatchButton8->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor8" ), QVariant( QColor() ) ).value<QColor>() );
212 mSwatchButton9->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor9" ), QVariant( QColor() ) ).value<QColor>() );
213 mSwatchButton10->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor10" ), QVariant( QColor() ) ).value<QColor>() );
214 mSwatchButton11->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor11" ), QVariant( QColor() ) ).value<QColor>() );
215 mSwatchButton12->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor12" ), QVariant( QColor() ) ).value<QColor>() );
216 mSwatchButton13->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor13" ), QVariant( QColor() ) ).value<QColor>() );
217 mSwatchButton14->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor14" ), QVariant( QColor() ) ).value<QColor>() );
218 mSwatchButton15->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor15" ), QVariant( QColor() ) ).value<QColor>() );
219 mSwatchButton16->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor16" ), QVariant( QColor() ) ).value<QColor>() );
220
221 //restore sample radius
222 mSpinBoxRadius->setValue( settings.value( QStringLiteral( "Windows/ColorDialog/sampleRadius" ), 1 ).toInt() );
223 mSamplePreview->setColor( QColor() );
224
225 // hidpi friendly sizes
226 const int swatchWidth = static_cast<int>( std::round( std::max( Qgis::UI_SCALE_FACTOR * 1.9 * mSwatchButton1->fontMetrics().height(), 38.0 ) ) );
227 const int swatchHeight = static_cast<int>( std::round( std::max( Qgis::UI_SCALE_FACTOR * 1.5 * mSwatchButton1->fontMetrics().height(), 30.0 ) ) );
228 mSwatchButton1->setMinimumSize( swatchWidth, swatchHeight );
229 mSwatchButton1->setMaximumSize( swatchWidth, swatchHeight );
230 mSwatchButton2->setMinimumSize( swatchWidth, swatchHeight );
231 mSwatchButton2->setMaximumSize( swatchWidth, swatchHeight );
232 mSwatchButton3->setMinimumSize( swatchWidth, swatchHeight );
233 mSwatchButton3->setMaximumSize( swatchWidth, swatchHeight );
234 mSwatchButton4->setMinimumSize( swatchWidth, swatchHeight );
235 mSwatchButton4->setMaximumSize( swatchWidth, swatchHeight );
236 mSwatchButton5->setMinimumSize( swatchWidth, swatchHeight );
237 mSwatchButton5->setMaximumSize( swatchWidth, swatchHeight );
238 mSwatchButton6->setMinimumSize( swatchWidth, swatchHeight );
239 mSwatchButton6->setMaximumSize( swatchWidth, swatchHeight );
240 mSwatchButton7->setMinimumSize( swatchWidth, swatchHeight );
241 mSwatchButton7->setMaximumSize( swatchWidth, swatchHeight );
242 mSwatchButton8->setMinimumSize( swatchWidth, swatchHeight );
243 mSwatchButton8->setMaximumSize( swatchWidth, swatchHeight );
244 mSwatchButton9->setMinimumSize( swatchWidth, swatchHeight );
245 mSwatchButton9->setMaximumSize( swatchWidth, swatchHeight );
246 mSwatchButton10->setMinimumSize( swatchWidth, swatchHeight );
247 mSwatchButton10->setMaximumSize( swatchWidth, swatchHeight );
248 mSwatchButton11->setMinimumSize( swatchWidth, swatchHeight );
249 mSwatchButton11->setMaximumSize( swatchWidth, swatchHeight );
250 mSwatchButton12->setMinimumSize( swatchWidth, swatchHeight );
251 mSwatchButton12->setMaximumSize( swatchWidth, swatchHeight );
252 mSwatchButton13->setMinimumSize( swatchWidth, swatchHeight );
253 mSwatchButton13->setMaximumSize( swatchWidth, swatchHeight );
254 mSwatchButton14->setMinimumSize( swatchWidth, swatchHeight );
255 mSwatchButton14->setMaximumSize( swatchWidth, swatchHeight );
256 mSwatchButton15->setMinimumSize( swatchWidth, swatchHeight );
257 mSwatchButton15->setMaximumSize( swatchWidth, swatchHeight );
258 mSwatchButton16->setMinimumSize( swatchWidth, swatchHeight );
259 mSwatchButton16->setMaximumSize( swatchWidth, swatchHeight );
260 const int previewHeight = static_cast<int>( std::round( std::max( Qgis::UI_SCALE_FACTOR * 2.0 * mSwatchButton1->fontMetrics().height(), 40.0 ) ) );
261 mColorPreview->setMinimumSize( 0, previewHeight );
262 mPreviewWidget->setMaximumHeight( previewHeight * 2 );
263 const int swatchAddSize = static_cast<int>( std::round( std::max( Qgis::UI_SCALE_FACTOR * 1.4 * mSwatchButton1->fontMetrics().height(), 28.0 ) ) );
264 mAddCustomColorButton->setMinimumWidth( swatchAddSize );
265 mAddCustomColorButton->setMaximumWidth( swatchAddSize );
266
267 const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
268 mTabWidget->setIconSize( QSize( iconSize, iconSize ) );
269
270 setColor( color );
271
272 // restore active Rgb/Cmyk component radio button
273 const int activeRgbRadio = settings.value( QStringLiteral( "Windows/ColorDialog/activeComponent" ), 2 ).toInt();
274 if ( QAbstractButton *rgbRadio = mRgbGroup->button( activeRgbRadio ) )
275 rgbRadio->setChecked( true );
276
277 const int activeCmykRadio = settings.value( QStringLiteral( "Windows/ColorDialog/activeCmykComponent" ), 0 ).toInt();
278 if ( QAbstractButton *cmykRadio = mCmykGroup->button( activeCmykRadio ) )
279 cmykRadio->setChecked( true );
280
281 const int currentTab = settings.value( QStringLiteral( "Windows/ColorDialog/activeTab" ), 0 ).toInt();
282 mTabWidget->setCurrentIndex( currentTab );
283
284 //setup connections
285 connect( mColorBox, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
286 connect( mColorWheel, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
287 connect( mColorText, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
288 connect( mVerticalRamp, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
289 connect( mRedSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
290 connect( mGreenSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
291 connect( mBlueSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
292 connect( mHueSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
293 connect( mValueSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
294 connect( mCyanSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
295 connect( mMagentaSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
296 connect( mYellowSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
297 connect( mBlackSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
298 connect( mSaturationSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
299 connect( mAlphaSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
300 connect( mColorPreview, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
301 connect( mSwatchButton1, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
302 connect( mSwatchButton2, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
303 connect( mSwatchButton3, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
304 connect( mSwatchButton4, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
305 connect( mSwatchButton5, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
306 connect( mSwatchButton6, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
307 connect( mSwatchButton7, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
308 connect( mSwatchButton8, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
309 connect( mSwatchButton9, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
310 connect( mSwatchButton10, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
311 connect( mSwatchButton11, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
312 connect( mSwatchButton12, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
313 connect( mSwatchButton13, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
314 connect( mSwatchButton14, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
315 connect( mSwatchButton15, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
316 connect( mSwatchButton16, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
317}
318
326
328{
329 //all widgets should have the same color, so it shouldn't matter
330 //which we fetch it from
331 return mColorPreview->color();
332}
333
334void QgsCompoundColorWidget::setAllowOpacity( const bool allowOpacity )
335{
336 mAllowAlpha = allowOpacity;
337 mAlphaLabel->setVisible( allowOpacity );
338 mAlphaSlider->setVisible( allowOpacity );
339 mColorText->setAllowOpacity( allowOpacity );
340 if ( !allowOpacity )
341 {
342 mAlphaLayout->setContentsMargins( 0, 0, 0, 0 );
343 mAlphaLayout->setSpacing( 0 );
344 }
345}
346
348{
349 mColorModel->setVisible( colorModelEditable );
350}
351
352void QgsCompoundColorWidget::refreshSchemeComboBox()
353{
354 mSchemeComboBox->blockSignals( true );
355 mSchemeComboBox->clear();
356 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
357 QList<QgsColorScheme *>::const_iterator schemeIt = schemeList.constBegin();
358 for ( ; schemeIt != schemeList.constEnd(); ++schemeIt )
359 {
360 mSchemeComboBox->addItem( ( *schemeIt )->schemeName() );
361 }
362 mSchemeComboBox->blockSignals( false );
363}
364
365
367{
368 QgsSettings s;
369 const QString lastDir = s.value( QStringLiteral( "/UI/lastGplPaletteDir" ), QDir::homePath() ).toString();
370 const QString filePath = QFileDialog::getOpenFileName( parent, tr( "Select Palette File" ), lastDir, QStringLiteral( "GPL (*.gpl);;All files (*.*)" ) );
371 if ( parent )
372 parent->activateWindow();
373 if ( filePath.isEmpty() )
374 {
375 return nullptr;
376 }
377
378 //check if file exists
379 const QFileInfo fileInfo( filePath );
380 if ( !fileInfo.exists() || !fileInfo.isReadable() )
381 {
382 QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "Error, file does not exist or is not readable." ) );
383 return nullptr;
384 }
385
386 s.setValue( QStringLiteral( "/UI/lastGplPaletteDir" ), fileInfo.absolutePath() );
387 QFile file( filePath );
388
389 QgsNamedColorList importedColors;
390 bool ok = false;
391 QString paletteName;
392 importedColors = QgsSymbolLayerUtils::importColorsFromGpl( file, ok, paletteName );
393 if ( !ok )
394 {
395 QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "Palette file is not readable." ) );
396 return nullptr;
397 }
398
399 if ( importedColors.length() == 0 )
400 {
401 //no imported colors
402 QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "No colors found in palette file." ) );
403 return nullptr;
404 }
405
406 //TODO - handle conflicting file names, name for new palette
407 QgsUserColorScheme *importedScheme = new QgsUserColorScheme( fileInfo.fileName() );
408 importedScheme->setName( paletteName );
409 importedScheme->setColors( importedColors );
410
412 return importedScheme;
413}
414
415void QgsCompoundColorWidget::importPalette()
416{
417 if ( importUserPaletteFromFile( this ) )
418 {
419 //refresh combobox
420 refreshSchemeComboBox();
421 mSchemeComboBox->setCurrentIndex( mSchemeComboBox->count() - 1 );
422 }
423}
424
425
427{
428 if ( QMessageBox::question( parent, tr( "Remove Color Palette" ), tr( "Are you sure you want to remove %1?" ).arg( scheme->schemeName() ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
429 {
430 //user canceled
431 return false;
432 }
433
434 //remove palette and associated gpl file
435 if ( !scheme->erase() )
436 {
437 //something went wrong
438 return false;
439 }
440
441 //remove scheme from registry
443 return true;
444}
445
446void QgsCompoundColorWidget::removePalette()
447{
448 //get current scheme
449 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
450 int prevIndex = mSchemeComboBox->currentIndex();
451 if ( prevIndex >= schemeList.length() )
452 {
453 return;
454 }
455
456 //make user scheme is a user removable scheme
457 QgsUserColorScheme *userScheme = dynamic_cast<QgsUserColorScheme *>( schemeList.at( prevIndex ) );
458 if ( !userScheme )
459 {
460 return;
461 }
462
463 if ( removeUserPalette( userScheme, this ) )
464 {
465 refreshSchemeComboBox();
466 prevIndex = std::max( std::min( prevIndex, mSchemeComboBox->count() - 1 ), 0 );
467 mSchemeComboBox->setCurrentIndex( prevIndex );
468 }
469}
470
472{
473 bool ok = false;
474 const QString name = QInputDialog::getText( parent, tr( "Create New Palette" ), tr( "Enter a name for the new palette:" ), QLineEdit::Normal, tr( "New palette" ), &ok );
475
476 if ( !ok || name.isEmpty() )
477 {
478 //user canceled
479 return nullptr;
480 }
481
482 //generate file name for new palette
483 const QDir palettePath( gplFilePath() );
484 const thread_local QRegularExpression badChars( "[,^@={}\\[\\]~!?:&*\"|#%<>$\"'();`' /\\\\]" );
485 QString filename = name.simplified().toLower().replace( badChars, QStringLiteral( "_" ) );
486 if ( filename.isEmpty() )
487 {
488 filename = tr( "new_palette" );
489 }
490 QFileInfo destFileInfo( palettePath.filePath( filename + ".gpl" ) );
491 int fileNumber = 1;
492 while ( destFileInfo.exists() )
493 {
494 //try to generate a unique file name
495 destFileInfo = QFileInfo( palettePath.filePath( filename + QStringLiteral( "%1.gpl" ).arg( fileNumber ) ) );
496 fileNumber++;
497 }
498
499 QgsUserColorScheme *newScheme = new QgsUserColorScheme( destFileInfo.fileName() );
500 newScheme->setName( name );
501
503 return newScheme;
504}
505
506void QgsCompoundColorWidget::newPalette()
507{
508 if ( createNewUserPalette( this ) )
509 {
510 //refresh combobox and set new scheme as active
511 refreshSchemeComboBox();
512 mSchemeComboBox->setCurrentIndex( mSchemeComboBox->count() - 1 );
513 }
514}
515
516QString QgsCompoundColorWidget::gplFilePath()
517{
518 QString palettesDir = QgsApplication::qgisSettingsDirPath() + "palettes";
519
520 const QDir localDir;
521 if ( !localDir.mkpath( palettesDir ) )
522 {
523 return QString();
524 }
525
526 return palettesDir;
527}
528
529void QgsCompoundColorWidget::schemeIndexChanged( int index )
530{
531 //save changes to scheme
532 if ( mSchemeList->isDirty() )
533 {
534 mSchemeList->saveColorsToScheme();
535 }
536
537 //get schemes with ShowInColorDialog set
538 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
539 if ( index >= schemeList.length() )
540 {
541 return;
542 }
543
544 QgsColorScheme *scheme = schemeList.at( index );
545 mSchemeList->setScheme( scheme );
546
547 updateActionsForCurrentScheme();
548
549 //copy action defaults to disabled
550 mActionCopyColors->setEnabled( false );
551}
552
553void QgsCompoundColorWidget::listSelectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
554{
555 Q_UNUSED( deselected )
556 mActionCopyColors->setEnabled( selected.length() > 0 );
557}
558
559void QgsCompoundColorWidget::mAddCustomColorButton_clicked()
560{
561 switch ( mLastCustomColorIndex )
562 {
563 case 0:
564 mSwatchButton1->setColor( mColorPreview->color() );
565 break;
566 case 1:
567 mSwatchButton2->setColor( mColorPreview->color() );
568 break;
569 case 2:
570 mSwatchButton3->setColor( mColorPreview->color() );
571 break;
572 case 3:
573 mSwatchButton4->setColor( mColorPreview->color() );
574 break;
575 case 4:
576 mSwatchButton5->setColor( mColorPreview->color() );
577 break;
578 case 5:
579 mSwatchButton6->setColor( mColorPreview->color() );
580 break;
581 case 6:
582 mSwatchButton7->setColor( mColorPreview->color() );
583 break;
584 case 7:
585 mSwatchButton8->setColor( mColorPreview->color() );
586 break;
587 case 8:
588 mSwatchButton9->setColor( mColorPreview->color() );
589 break;
590 case 9:
591 mSwatchButton10->setColor( mColorPreview->color() );
592 break;
593 case 10:
594 mSwatchButton11->setColor( mColorPreview->color() );
595 break;
596 case 11:
597 mSwatchButton12->setColor( mColorPreview->color() );
598 break;
599 case 12:
600 mSwatchButton13->setColor( mColorPreview->color() );
601 break;
602 case 13:
603 mSwatchButton14->setColor( mColorPreview->color() );
604 break;
605 case 14:
606 mSwatchButton15->setColor( mColorPreview->color() );
607 break;
608 case 15:
609 mSwatchButton16->setColor( mColorPreview->color() );
610 break;
611 }
612 mLastCustomColorIndex++;
613 if ( mLastCustomColorIndex >= 16 )
614 {
615 mLastCustomColorIndex = 0;
616 }
617}
618
619void QgsCompoundColorWidget::mSampleButton_clicked()
620{
621 //activate picker color
623 grabMouse();
624 grabKeyboard();
625 mPickingColor = true;
626 setMouseTracking( true );
627}
628
629void QgsCompoundColorWidget::mTabWidget_currentChanged( int index )
630{
631 //disable radio buttons if not using the first tab, as they have no meaning for other tabs
632 const bool enabled = index == 0;
633 const QList<QRadioButton *> colorRadios { mHueRadio, mSaturationRadio, mValueRadio, mRedRadio, mGreenRadio, mBlueRadio, mCyanRadio, mMagentaRadio, mYellowRadio, mBlackRadio };
634 for ( QRadioButton *colorRadio : colorRadios )
635 colorRadio->setEnabled( enabled );
636}
637
638void QgsCompoundColorWidget::mActionShowInButtons_toggled( bool state )
639{
640 QgsUserColorScheme *scheme = dynamic_cast<QgsUserColorScheme *>( mSchemeList->scheme() );
641 if ( scheme )
642 {
643 scheme->setShowSchemeInMenu( state );
644 }
645}
646
647QScreen *QgsCompoundColorWidget::findScreenAt( QPoint pos )
648{
649 const QList<QScreen *> screens = QGuiApplication::screens();
650 for ( QScreen *screen : screens )
651 {
652 if ( screen->geometry().contains( pos ) )
653 {
654 return screen;
655 }
656 }
657 return nullptr;
658}
659
660void QgsCompoundColorWidget::saveSettings()
661{
662 //save changes to scheme
663 if ( mSchemeList->isDirty() )
664 {
665 mSchemeList->saveColorsToScheme();
666 }
667
668 QgsSettings settings;
669
670 // record active component
671 settings.setValue( QStringLiteral( "Windows/ColorDialog/activeComponent" ), mRgbGroup->checkedId() );
672 settings.setValue( QStringLiteral( "Windows/ColorDialog/activeCmykComponent" ), mCmykGroup->checkedId() );
673
674 //record current scheme
675 settings.setValue( QStringLiteral( "Windows/ColorDialog/activeScheme" ), mSchemeComboBox->currentIndex() );
676
677 //record current tab
678 settings.setValue( QStringLiteral( "Windows/ColorDialog/activeTab" ), mTabWidget->currentIndex() );
679
680 //record custom colors
681 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor1" ), QVariant( mSwatchButton1->color() ) );
682 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor2" ), QVariant( mSwatchButton2->color() ) );
683 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor3" ), QVariant( mSwatchButton3->color() ) );
684 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor4" ), QVariant( mSwatchButton4->color() ) );
685 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor5" ), QVariant( mSwatchButton5->color() ) );
686 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor6" ), QVariant( mSwatchButton6->color() ) );
687 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor7" ), QVariant( mSwatchButton7->color() ) );
688 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor8" ), QVariant( mSwatchButton8->color() ) );
689 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor9" ), QVariant( mSwatchButton9->color() ) );
690 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor10" ), QVariant( mSwatchButton10->color() ) );
691 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor11" ), QVariant( mSwatchButton11->color() ) );
692 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor12" ), QVariant( mSwatchButton12->color() ) );
693 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor13" ), QVariant( mSwatchButton13->color() ) );
694 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor14" ), QVariant( mSwatchButton14->color() ) );
695 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor15" ), QVariant( mSwatchButton15->color() ) );
696 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor16" ), QVariant( mSwatchButton16->color() ) );
697
698 //sample radius
699 settings.setValue( QStringLiteral( "Windows/ColorDialog/sampleRadius" ), mSpinBoxRadius->value() );
700}
701
702void QgsCompoundColorWidget::stopPicking( QPoint eventPos, const bool takeSample )
703{
704 //release mouse and keyboard, and reset cursor
705 releaseMouse();
706 releaseKeyboard();
707 unsetCursor();
708 setMouseTracking( false );
709 mPickingColor = false;
710
711 if ( !takeSample )
712 {
713 //not sampling color, nothing more to do
714 return;
715 }
716
717 //grab snapshot of pixel under mouse cursor
718 const QColor snappedColor = sampleColor( eventPos );
719 mSamplePreview->setColor( snappedColor );
720 mColorPreview->setColor( snappedColor, true );
721}
722
723void QgsCompoundColorWidget::setColor( const QColor &color )
724{
725 const QColor::Spec colorSpec = color.spec() == QColor::Cmyk ? QColor::Cmyk : QColor::Rgb;
726 mColorModel->setCurrentIndex( mColorModel->findData( colorSpec ) );
727 mRGB->setVisible( colorSpec != QColor::Cmyk );
728 mCMYK->setVisible( colorSpec == QColor::Cmyk );
729 _setColor( color );
730}
731
732void QgsCompoundColorWidget::_setColor( const QColor &color )
733{
734 if ( !color.isValid() )
735 {
736 return;
737 }
738
739 QColor fixedColor = QColor( color );
740 if ( !mAllowAlpha )
741 {
742 //opacity disallowed, so don't permit transparent colors
743 fixedColor.setAlpha( 255 );
744 }
745
746 if ( mColorModel->currentIndex() && fixedColor.spec() != QColor::Cmyk )
747 {
748 fixedColor = fixedColor.toCmyk();
749 }
750
751 const QList<QgsColorWidget *> colorWidgets = this->findChildren<QgsColorWidget *>();
752 const auto constColorWidgets = colorWidgets;
753 for ( QgsColorWidget *widget : constColorWidgets )
754 {
755 if ( widget == mSamplePreview )
756 {
757 continue;
758 }
759 widget->blockSignals( true );
760 widget->setColor( fixedColor );
761 widget->blockSignals( false );
762 }
763
764
765 emit currentColorChanged( fixedColor );
766}
767
769{
770 mOldColorLabel->setVisible( color.isValid() );
771 mColorPreview->setColor2( color );
772}
773
775{
776 saveSettings();
777 QWidget::hideEvent( e );
778}
779
781{
782 if ( mPickingColor )
783 {
784 //don't show dialog if in color picker mode
785 e->accept();
786 return;
787 }
788
789 QWidget::mousePressEvent( e );
790}
791
792QColor QgsCompoundColorWidget::averageColor( const QImage &image ) const
793{
794 QRgb tmpRgb;
795 int colorCount = 0;
796 int sumRed = 0;
797 int sumBlue = 0;
798 int sumGreen = 0;
799 //scan through image and sum rgb components
800 for ( int heightIndex = 0; heightIndex < image.height(); ++heightIndex )
801 {
802 const QRgb *scanLine = reinterpret_cast<const QRgb *>( image.constScanLine( heightIndex ) );
803 for ( int widthIndex = 0; widthIndex < image.width(); ++widthIndex )
804 {
805 tmpRgb = scanLine[widthIndex];
806 sumRed += qRed( tmpRgb );
807 sumBlue += qBlue( tmpRgb );
808 sumGreen += qGreen( tmpRgb );
809 colorCount++;
810 }
811 }
812 //calculate average components as floats
813 const double avgRed = static_cast<double>( sumRed ) / ( 255.0 * colorCount );
814 const double avgGreen = static_cast<double>( sumGreen ) / ( 255.0 * colorCount );
815 const double avgBlue = static_cast<double>( sumBlue ) / ( 255.0 * colorCount );
816
817 //create a new color representing the average
818 return QColor::fromRgbF( avgRed, avgGreen, avgBlue );
819}
820
821QColor QgsCompoundColorWidget::sampleColor( QPoint point ) const
822{
823 const int sampleRadius = mSpinBoxRadius->value() - 1;
824 QScreen *screen = findScreenAt( point );
825 if ( !screen )
826 {
827 return QColor();
828 }
829
830 const int x = point.x() - screen->geometry().left();
831 const int y = point.y() - screen->geometry().top();
832 const QPixmap snappedPixmap = screen->grabWindow( 0, x - sampleRadius, y - sampleRadius, 1 + sampleRadius * 2, 1 + sampleRadius * 2 );
833 const QImage snappedImage = snappedPixmap.toImage();
834 //scan all pixels and take average color
835 return averageColor( snappedImage );
836}
837
839{
840 if ( mPickingColor )
841 {
842 //currently in color picker mode
843 //sample color under cursor update preview widget to give feedback to user
844 const QColor hoverColor = sampleColor( e->globalPos() );
845 mSamplePreview->setColor( hoverColor );
846
847 e->accept();
848 return;
849 }
850
851 QWidget::mouseMoveEvent( e );
852}
853
855{
856 if ( mPickingColor )
857 {
858 //end color picking operation by sampling the color under cursor
859 stopPicking( e->globalPos() );
860 e->accept();
861 return;
862 }
863
864 QWidget::mouseReleaseEvent( e );
865}
866
868{
869 if ( !mPickingColor )
870 {
871 //if not picking a color, use default tool button behavior
873 return;
874 }
875
876 //cancel picking, sampling the color if space was pressed
877 stopPicking( QCursor::pos(), e->key() == Qt::Key_Space );
878}
879
880
881void QgsCompoundColorWidget::updateComponent()
882{
883 const bool isCmyk = mColorModel->currentData().toInt() == QColor::Spec::Cmyk;
884 const auto radios = isCmyk ? mCmykRadios : mRgbRadios;
885 const QButtonGroup *group = isCmyk ? mCmykGroup : mRgbGroup;
886
887 const int id = group->checkedId();
888 if ( id >= 0 && id < radios.count() )
889 {
890 const QgsColorWidget::ColorComponent component = radios.at( group->checkedId() ).second;
891 mColorBox->setComponent( component );
892 mVerticalRamp->setComponent( component );
893 }
894}
895
896void QgsCompoundColorWidget::onColorButtonGroupToggled( int, bool checked )
897{
898 if ( checked )
899 updateComponent();
900}
901
902void QgsCompoundColorWidget::mAddColorToSchemeButton_clicked()
903{
904 mSchemeList->addColor( mColorPreview->color(), QgsSymbolLayerUtils::colorToName( mColorPreview->color() ) );
905}
906
907void QgsCompoundColorWidget::updateActionsForCurrentScheme()
908{
909 QgsColorScheme *scheme = mSchemeList->scheme();
910
911 mActionImportColors->setEnabled( scheme->isEditable() );
912 mActionPasteColors->setEnabled( scheme->isEditable() );
913 mAddColorToSchemeButton->setEnabled( scheme->isEditable() );
914 mRemoveColorsFromSchemeButton->setEnabled( scheme->isEditable() );
915
916 QgsUserColorScheme *userScheme = dynamic_cast<QgsUserColorScheme *>( scheme );
917 mActionRemovePalette->setEnabled( static_cast<bool>( userScheme ) );
918 if ( userScheme )
919 {
920 mActionShowInButtons->setEnabled( true );
921 whileBlocking( mActionShowInButtons )->setChecked( userScheme->flags() & QgsColorScheme::ShowInColorButtonMenu );
922 }
923 else
924 {
925 whileBlocking( mActionShowInButtons )->setChecked( false );
926 mActionShowInButtons->setEnabled( false );
927 }
928}
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:5775
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
static QgsColorSchemeRegistry * colorSchemeRegistry()
Returns the application's color scheme registry, used for managing color schemes.
static QString qgisSettingsDirPath()
Returns the path to the settings directory in user's home dir.
@ Sampler
Color/Value picker.
@ SignalOnly
Emit colorClicked signal only, no dialog.
void colorClicked(const QColor &color)
Emitted when the button is clicked, if the button's behavior is set to SignalOnly.
@ Vertical
Vertical ramp.
void pasteColors()
Pastes colors from clipboard to the list.
void removeSelection()
Removes any selected colors from the list.
void copyColors()
Copies colors from the list to the clipboard.
void showExportColorsDialog()
Displays a file picker dialog allowing users to export colors from the list into a file.
void colorSelected(const QColor &color)
Emitted when a color is selected from the list.
void showImportColorsDialog()
Displays a file picker dialog allowing users to import colors into the list from a file.
void addColorScheme(QgsColorScheme *scheme)
Adds a color scheme to the registry.
QList< QgsColorScheme * > schemes() const
Returns all color schemes in the registry.
bool removeColorScheme(QgsColorScheme *scheme)
Removes all matching color schemes from the registry.
Abstract base class for color schemes.
@ ShowInColorButtonMenu
Show scheme in color button drop-down menu.
@ ShowInColorDialog
Show scheme in color picker dialog.
virtual bool isEditable() const
Returns whether the color scheme is editable.
A base class for interactive color widgets.
void colorChanged(const QColor &color)
Emitted when the widget's color changes.
ColorComponent
Specifies the color component which the widget alters.
@ Hue
Hue component of color (based on HSV model)
@ Alpha
Alpha component (opacity) of color.
@ Green
Green component of color.
@ Red
Red component of color.
@ Saturation
Saturation component of color (based on HSV model)
@ Magenta
Magenta component (based on CMYK model) of color.
@ Yellow
Yellow component (based on CMYK model) of color.
@ Black
Black component (based on CMYK model) of color.
@ Cyan
Cyan component (based on CMYK model) of color.
@ Blue
Blue component of color.
@ Value
Value component of color (based on HSV model)
@ LayoutVertical
Use a narrower, vertically stacked layout.
void currentColorChanged(const QColor &color)
Emitted when the dialog's color changes.
void hideEvent(QHideEvent *e) override
QgsCompoundColorWidget(QWidget *parent=nullptr, const QColor &color=QColor(), Layout layout=LayoutDefault)
Constructor for QgsCompoundColorWidget.
void mousePressEvent(QMouseEvent *e) override
void setColorModelEditable(bool colorModelEditable)
Sets whether color model is editable or not.
static QgsUserColorScheme * importUserPaletteFromFile(QWidget *parent)
Triggers a user prompt for importing a new color scheme from an existing GPL file.
void setPreviousColor(const QColor &color)
Sets the color to show in an optional "previous color" section.
static bool removeUserPalette(QgsUserColorScheme *scheme, QWidget *parent)
Triggers a user prompt for removing an existing user color scheme.
static QgsUserColorScheme * createNewUserPalette(QWidget *parent)
Triggers a user prompt for creating a new user color scheme.
void mouseMoveEvent(QMouseEvent *e) override
void setAllowOpacity(bool allowOpacity)
Sets whether opacity modification (transparency) is permitted for the color dialog.
void setColor(const QColor &color)
Sets the current color for the dialog.
void keyPressEvent(QKeyEvent *e) override
QColor color() const
Returns the current color for the dialog.
void mouseReleaseEvent(QMouseEvent *e) override
bool setColors(const QgsNamedColorList &colors, const QString &context=QString(), const QColor &baseColor=QColor()) override
Sets the colors for the scheme.
Base class for any widget that can be shown as a inline panel.
void keyPressEvent(QKeyEvent *event) override
Overridden key press event to handle the esc event on the widget.
static void addRecentColor(const QColor &color)
Adds a color to the list of recent colors.
A utility class for dynamic handling of changes to screen properties.
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
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.
static QgsNamedColorList importColorsFromGpl(QFile &file, bool &ok, QString &name)
Imports colors from a gpl GIMP palette file.
static QString colorToName(const QColor &color)
Returns a friendly display name for a color.
A color scheme which stores its colors in a gpl palette file within the "palettes" subfolder off the ...
QString schemeName() const override
Gets the name for the color scheme.
void setName(const QString &name)
Sets the name for the scheme.
QgsColorScheme::SchemeFlags flags() const override
Returns the current flags for the color scheme.
bool erase()
Erases the associated gpl palette file from the users "palettes" folder.
void setShowSchemeInMenu(bool show)
Sets whether a this scheme should be shown in color button menus.
QList< QPair< QColor, QString > > QgsNamedColorList
List of colors paired with a friendly display name identifying the color.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5970