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