QGIS API Documentation 3.39.0-Master (d85f3c2a281)
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 setColor( color );
273
274 // restore active Rgb/Cmyk component radio button
275 const int activeRgbRadio = settings.value( QStringLiteral( "Windows/ColorDialog/activeComponent" ), 2 ).toInt();
276 if ( QAbstractButton *rgbRadio = mRgbGroup->button( activeRgbRadio ) )
277 rgbRadio->setChecked( true );
278
279 const int activeCmykRadio = settings.value( QStringLiteral( "Windows/ColorDialog/activeCmykComponent" ), 0 ).toInt();
280 if ( QAbstractButton *cmykRadio = mCmykGroup->button( activeCmykRadio ) )
281 cmykRadio->setChecked( true );
282
283 const int currentTab = settings.value( QStringLiteral( "Windows/ColorDialog/activeTab" ), 0 ).toInt();
284 mTabWidget->setCurrentIndex( currentTab );
285
286 //setup connections
287 connect( mColorBox, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
288 connect( mColorWheel, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
289 connect( mColorText, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
290 connect( mVerticalRamp, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
291 connect( mRedSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
292 connect( mGreenSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
293 connect( mBlueSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
294 connect( mHueSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
295 connect( mValueSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
296 connect( mCyanSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
297 connect( mMagentaSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
298 connect( mYellowSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
299 connect( mBlackSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
300 connect( mSaturationSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
301 connect( mAlphaSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
302 connect( mColorPreview, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
303 connect( mSwatchButton1, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
304 connect( mSwatchButton2, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
305 connect( mSwatchButton3, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
306 connect( mSwatchButton4, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
307 connect( mSwatchButton5, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
308 connect( mSwatchButton6, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
309 connect( mSwatchButton7, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
310 connect( mSwatchButton8, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
311 connect( mSwatchButton9, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
312 connect( mSwatchButton10, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
313 connect( mSwatchButton11, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
314 connect( mSwatchButton12, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
315 connect( mSwatchButton13, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
316 connect( mSwatchButton14, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
317 connect( mSwatchButton15, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
318 connect( mSwatchButton16, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
319}
320
328
330{
331 //all widgets should have the same color, so it shouldn't matter
332 //which we fetch it from
333 return mColorPreview->color();
334}
335
336void QgsCompoundColorWidget::setAllowOpacity( const bool allowOpacity )
337{
338 mAllowAlpha = allowOpacity;
339 mAlphaLabel->setVisible( allowOpacity );
340 mAlphaSlider->setVisible( allowOpacity );
341 mColorText->setAllowOpacity( allowOpacity );
342 if ( !allowOpacity )
343 {
344 mAlphaLayout->setContentsMargins( 0, 0, 0, 0 );
345 mAlphaLayout->setSpacing( 0 );
346 }
347}
348
350{
351 mColorModel->setVisible( colorModelEditable );
352}
353
354void QgsCompoundColorWidget::refreshSchemeComboBox()
355{
356 mSchemeComboBox->blockSignals( true );
357 mSchemeComboBox->clear();
358 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
359 QList<QgsColorScheme *>::const_iterator schemeIt = schemeList.constBegin();
360 for ( ; schemeIt != schemeList.constEnd(); ++schemeIt )
361 {
362 mSchemeComboBox->addItem( ( *schemeIt )->schemeName() );
363 }
364 mSchemeComboBox->blockSignals( false );
365}
366
367
369{
370 QgsSettings s;
371 const QString lastDir = s.value( QStringLiteral( "/UI/lastGplPaletteDir" ), QDir::homePath() ).toString();
372 const QString filePath = QFileDialog::getOpenFileName( parent, tr( "Select Palette File" ), lastDir, QStringLiteral( "GPL (*.gpl);;All files (*.*)" ) );
373 if ( parent )
374 parent->activateWindow();
375 if ( filePath.isEmpty() )
376 {
377 return nullptr;
378 }
379
380 //check if file exists
381 const QFileInfo fileInfo( filePath );
382 if ( !fileInfo.exists() || !fileInfo.isReadable() )
383 {
384 QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "Error, file does not exist or is not readable." ) );
385 return nullptr;
386 }
387
388 s.setValue( QStringLiteral( "/UI/lastGplPaletteDir" ), fileInfo.absolutePath() );
389 QFile file( filePath );
390
391 QgsNamedColorList importedColors;
392 bool ok = false;
393 QString paletteName;
394 importedColors = QgsSymbolLayerUtils::importColorsFromGpl( file, ok, paletteName );
395 if ( !ok )
396 {
397 QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "Palette file is not readable." ) );
398 return nullptr;
399 }
400
401 if ( importedColors.length() == 0 )
402 {
403 //no imported colors
404 QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "No colors found in palette file." ) );
405 return nullptr;
406 }
407
408 //TODO - handle conflicting file names, name for new palette
409 QgsUserColorScheme *importedScheme = new QgsUserColorScheme( fileInfo.fileName() );
410 importedScheme->setName( paletteName );
411 importedScheme->setColors( importedColors );
412
414 return importedScheme;
415}
416
417void QgsCompoundColorWidget::importPalette()
418{
419 if ( importUserPaletteFromFile( this ) )
420 {
421 //refresh combobox
422 refreshSchemeComboBox();
423 mSchemeComboBox->setCurrentIndex( mSchemeComboBox->count() - 1 );
424 }
425}
426
427
429{
430 if ( QMessageBox::question( parent, tr( "Remove Color Palette" ),
431 tr( "Are you sure you want to remove %1?" ).arg( scheme->schemeName() ),
432 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
433 {
434 //user canceled
435 return false;
436 }
437
438 //remove palette and associated gpl file
439 if ( !scheme->erase() )
440 {
441 //something went wrong
442 return false;
443 }
444
445 //remove scheme from registry
447 return true;
448}
449
450void QgsCompoundColorWidget::removePalette()
451{
452 //get current scheme
453 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
454 int prevIndex = mSchemeComboBox->currentIndex();
455 if ( prevIndex >= schemeList.length() )
456 {
457 return;
458 }
459
460 //make user scheme is a user removable scheme
461 QgsUserColorScheme *userScheme = dynamic_cast<QgsUserColorScheme *>( schemeList.at( prevIndex ) );
462 if ( !userScheme )
463 {
464 return;
465 }
466
467 if ( removeUserPalette( userScheme, this ) )
468 {
469 refreshSchemeComboBox();
470 prevIndex = std::max( std::min( prevIndex, mSchemeComboBox->count() - 1 ), 0 );
471 mSchemeComboBox->setCurrentIndex( prevIndex );
472 }
473}
474
476{
477 bool ok = false;
478 const QString name = QInputDialog::getText( parent, tr( "Create New Palette" ), tr( "Enter a name for the new palette:" ),
479 QLineEdit::Normal, tr( "New palette" ), &ok );
480
481 if ( !ok || name.isEmpty() )
482 {
483 //user canceled
484 return nullptr;
485 }
486
487 //generate file name for new palette
488 const QDir palettePath( gplFilePath() );
489 const thread_local QRegularExpression badChars( "[,^@={}\\[\\]~!?:&*\"|#%<>$\"'();`' /\\\\]" );
490 QString filename = name.simplified().toLower().replace( badChars, QStringLiteral( "_" ) );
491 if ( filename.isEmpty() )
492 {
493 filename = tr( "new_palette" );
494 }
495 QFileInfo destFileInfo( palettePath.filePath( filename + ".gpl" ) );
496 int fileNumber = 1;
497 while ( destFileInfo.exists() )
498 {
499 //try to generate a unique file name
500 destFileInfo = QFileInfo( palettePath.filePath( filename + QStringLiteral( "%1.gpl" ).arg( fileNumber ) ) );
501 fileNumber++;
502 }
503
504 QgsUserColorScheme *newScheme = new QgsUserColorScheme( destFileInfo.fileName() );
505 newScheme->setName( name );
506
508 return newScheme;
509}
510
511void QgsCompoundColorWidget::newPalette()
512{
513 if ( createNewUserPalette( this ) )
514 {
515 //refresh combobox and set new scheme as active
516 refreshSchemeComboBox();
517 mSchemeComboBox->setCurrentIndex( mSchemeComboBox->count() - 1 );
518 }
519}
520
521QString QgsCompoundColorWidget::gplFilePath()
522{
523 QString palettesDir = QgsApplication::qgisSettingsDirPath() + "palettes";
524
525 const QDir localDir;
526 if ( !localDir.mkpath( palettesDir ) )
527 {
528 return QString();
529 }
530
531 return palettesDir;
532}
533
534void QgsCompoundColorWidget::schemeIndexChanged( int index )
535{
536 //save changes to scheme
537 if ( mSchemeList->isDirty() )
538 {
539 mSchemeList->saveColorsToScheme();
540 }
541
542 //get schemes with ShowInColorDialog set
543 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
544 if ( index >= schemeList.length() )
545 {
546 return;
547 }
548
549 QgsColorScheme *scheme = schemeList.at( index );
550 mSchemeList->setScheme( scheme );
551
552 updateActionsForCurrentScheme();
553
554 //copy action defaults to disabled
555 mActionCopyColors->setEnabled( false );
556}
557
558void QgsCompoundColorWidget::listSelectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
559{
560 Q_UNUSED( deselected )
561 mActionCopyColors->setEnabled( selected.length() > 0 );
562}
563
564void QgsCompoundColorWidget::mAddCustomColorButton_clicked()
565{
566 switch ( mLastCustomColorIndex )
567 {
568 case 0:
569 mSwatchButton1->setColor( mColorPreview->color() );
570 break;
571 case 1:
572 mSwatchButton2->setColor( mColorPreview->color() );
573 break;
574 case 2:
575 mSwatchButton3->setColor( mColorPreview->color() );
576 break;
577 case 3:
578 mSwatchButton4->setColor( mColorPreview->color() );
579 break;
580 case 4:
581 mSwatchButton5->setColor( mColorPreview->color() );
582 break;
583 case 5:
584 mSwatchButton6->setColor( mColorPreview->color() );
585 break;
586 case 6:
587 mSwatchButton7->setColor( mColorPreview->color() );
588 break;
589 case 7:
590 mSwatchButton8->setColor( mColorPreview->color() );
591 break;
592 case 8:
593 mSwatchButton9->setColor( mColorPreview->color() );
594 break;
595 case 9:
596 mSwatchButton10->setColor( mColorPreview->color() );
597 break;
598 case 10:
599 mSwatchButton11->setColor( mColorPreview->color() );
600 break;
601 case 11:
602 mSwatchButton12->setColor( mColorPreview->color() );
603 break;
604 case 12:
605 mSwatchButton13->setColor( mColorPreview->color() );
606 break;
607 case 13:
608 mSwatchButton14->setColor( mColorPreview->color() );
609 break;
610 case 14:
611 mSwatchButton15->setColor( mColorPreview->color() );
612 break;
613 case 15:
614 mSwatchButton16->setColor( mColorPreview->color() );
615 break;
616 }
617 mLastCustomColorIndex++;
618 if ( mLastCustomColorIndex >= 16 )
619 {
620 mLastCustomColorIndex = 0;
621 }
622}
623
624void QgsCompoundColorWidget::mSampleButton_clicked()
625{
626 //activate picker color
628 grabMouse();
629 grabKeyboard();
630 mPickingColor = true;
631 setMouseTracking( true );
632}
633
634void QgsCompoundColorWidget::mTabWidget_currentChanged( int index )
635{
636 //disable radio buttons if not using the first tab, as they have no meaning for other tabs
637 const bool enabled = index == 0;
638 const QList<QRadioButton *> colorRadios{ mHueRadio, mSaturationRadio, mValueRadio, mRedRadio, mGreenRadio, mBlueRadio, mCyanRadio, mMagentaRadio, mYellowRadio, mBlackRadio };
639 for ( QRadioButton *colorRadio : colorRadios )
640 colorRadio->setEnabled( enabled );
641}
642
643void QgsCompoundColorWidget::mActionShowInButtons_toggled( bool state )
644{
645 QgsUserColorScheme *scheme = dynamic_cast< QgsUserColorScheme * >( mSchemeList->scheme() );
646 if ( scheme )
647 {
648 scheme->setShowSchemeInMenu( state );
649 }
650}
651
652QScreen *QgsCompoundColorWidget::findScreenAt( QPoint pos )
653{
654 const QList< QScreen * > screens = QGuiApplication::screens();
655 for ( QScreen *screen : screens )
656 {
657 if ( screen->geometry().contains( pos ) )
658 {
659 return screen;
660 }
661 }
662 return nullptr;
663}
664
665void QgsCompoundColorWidget::saveSettings()
666{
667 //save changes to scheme
668 if ( mSchemeList->isDirty() )
669 {
670 mSchemeList->saveColorsToScheme();
671 }
672
673 QgsSettings settings;
674
675 // record active component
676 settings.setValue( QStringLiteral( "Windows/ColorDialog/activeComponent" ), mRgbGroup->checkedId() );
677 settings.setValue( QStringLiteral( "Windows/ColorDialog/activeCmykComponent" ), mCmykGroup->checkedId() );
678
679 //record current scheme
680 settings.setValue( QStringLiteral( "Windows/ColorDialog/activeScheme" ), mSchemeComboBox->currentIndex() );
681
682 //record current tab
683 settings.setValue( QStringLiteral( "Windows/ColorDialog/activeTab" ), mTabWidget->currentIndex() );
684
685 //record custom colors
686 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor1" ), QVariant( mSwatchButton1->color() ) );
687 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor2" ), QVariant( mSwatchButton2->color() ) );
688 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor3" ), QVariant( mSwatchButton3->color() ) );
689 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor4" ), QVariant( mSwatchButton4->color() ) );
690 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor5" ), QVariant( mSwatchButton5->color() ) );
691 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor6" ), QVariant( mSwatchButton6->color() ) );
692 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor7" ), QVariant( mSwatchButton7->color() ) );
693 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor8" ), QVariant( mSwatchButton8->color() ) );
694 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor9" ), QVariant( mSwatchButton9->color() ) );
695 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor10" ), QVariant( mSwatchButton10->color() ) );
696 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor11" ), QVariant( mSwatchButton11->color() ) );
697 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor12" ), QVariant( mSwatchButton12->color() ) );
698 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor13" ), QVariant( mSwatchButton13->color() ) );
699 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor14" ), QVariant( mSwatchButton14->color() ) );
700 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor15" ), QVariant( mSwatchButton15->color() ) );
701 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor16" ), QVariant( mSwatchButton16->color() ) );
702
703 //sample radius
704 settings.setValue( QStringLiteral( "Windows/ColorDialog/sampleRadius" ), mSpinBoxRadius->value() );
705}
706
707void QgsCompoundColorWidget::stopPicking( QPoint eventPos, const bool takeSample )
708{
709 //release mouse and keyboard, and reset cursor
710 releaseMouse();
711 releaseKeyboard();
712 unsetCursor();
713 setMouseTracking( false );
714 mPickingColor = false;
715
716 if ( !takeSample )
717 {
718 //not sampling color, nothing more to do
719 return;
720 }
721
722 //grab snapshot of pixel under mouse cursor
723 const QColor snappedColor = sampleColor( eventPos );
724 mSamplePreview->setColor( snappedColor );
725 mColorPreview->setColor( snappedColor, true );
726}
727
728void QgsCompoundColorWidget::setColor( const QColor &color )
729{
730 const QColor::Spec colorSpec = color.spec() == QColor::Cmyk ? QColor::Cmyk : QColor::Rgb;
731 mColorModel->setCurrentIndex( mColorModel->findData( colorSpec ) );
732 mRGB->setVisible( colorSpec != QColor::Cmyk );
733 mCMYK->setVisible( colorSpec == QColor::Cmyk );
734 _setColor( color );
735}
736
737void QgsCompoundColorWidget::_setColor( const QColor &color )
738{
739 if ( !color.isValid() )
740 {
741 return;
742 }
743
744 QColor fixedColor = QColor( color );
745 if ( !mAllowAlpha )
746 {
747 //opacity disallowed, so don't permit transparent colors
748 fixedColor.setAlpha( 255 );
749 }
750
751 if ( mColorModel->currentIndex() && fixedColor.spec() != QColor::Cmyk )
752 {
753 fixedColor = fixedColor.toCmyk();
754 }
755
756 const QList<QgsColorWidget *> colorWidgets = this->findChildren<QgsColorWidget *>();
757 const auto constColorWidgets = colorWidgets;
758 for ( QgsColorWidget *widget : constColorWidgets )
759 {
760 if ( widget == mSamplePreview )
761 {
762 continue;
763 }
764 widget->blockSignals( true );
765 widget->setColor( fixedColor );
766 widget->blockSignals( false );
767 }
768
769
770 emit currentColorChanged( fixedColor );
771}
772
774{
775 mOldColorLabel->setVisible( color.isValid() );
776 mColorPreview->setColor2( color );
777}
778
780{
781 saveSettings();
782 QWidget::hideEvent( e );
783}
784
786{
787 if ( mPickingColor )
788 {
789 //don't show dialog if in color picker mode
790 e->accept();
791 return;
792 }
793
794 QWidget::mousePressEvent( e );
795}
796
797QColor QgsCompoundColorWidget::averageColor( const QImage &image ) const
798{
799 QRgb tmpRgb;
800 int colorCount = 0;
801 int sumRed = 0;
802 int sumBlue = 0;
803 int sumGreen = 0;
804 //scan through image and sum rgb components
805 for ( int heightIndex = 0; heightIndex < image.height(); ++heightIndex )
806 {
807 const QRgb *scanLine = reinterpret_cast< const QRgb * >( image.constScanLine( heightIndex ) );
808 for ( int widthIndex = 0; widthIndex < image.width(); ++widthIndex )
809 {
810 tmpRgb = scanLine[widthIndex];
811 sumRed += qRed( tmpRgb );
812 sumBlue += qBlue( tmpRgb );
813 sumGreen += qGreen( tmpRgb );
814 colorCount++;
815 }
816 }
817 //calculate average components as floats
818 const double avgRed = static_cast<double>( sumRed ) / ( 255.0 * colorCount );
819 const double avgGreen = static_cast<double>( sumGreen ) / ( 255.0 * colorCount );
820 const double avgBlue = static_cast<double>( sumBlue ) / ( 255.0 * colorCount );
821
822 //create a new color representing the average
823 return QColor::fromRgbF( avgRed, avgGreen, avgBlue );
824}
825
826QColor QgsCompoundColorWidget::sampleColor( QPoint point ) const
827{
828 const int sampleRadius = mSpinBoxRadius->value() - 1;
829 QScreen *screen = findScreenAt( point );
830 if ( ! screen )
831 {
832 return QColor();
833 }
834
835 const int x = point.x() - screen->geometry().left();
836 const int y = point.y() - screen->geometry().top();
837 const QPixmap snappedPixmap = screen->grabWindow( 0,
838 x - sampleRadius,
839 y - sampleRadius,
840 1 + sampleRadius * 2,
841 1 + sampleRadius * 2 );
842 const QImage snappedImage = snappedPixmap.toImage();
843 //scan all pixels and take average color
844 return averageColor( snappedImage );
845}
846
848{
849 if ( mPickingColor )
850 {
851 //currently in color picker mode
852 //sample color under cursor update preview widget to give feedback to user
853 const QColor hoverColor = sampleColor( e->globalPos() );
854 mSamplePreview->setColor( hoverColor );
855
856 e->accept();
857 return;
858 }
859
860 QWidget::mouseMoveEvent( e );
861}
862
864{
865 if ( mPickingColor )
866 {
867 //end color picking operation by sampling the color under cursor
868 stopPicking( e->globalPos() );
869 e->accept();
870 return;
871 }
872
873 QWidget::mouseReleaseEvent( e );
874}
875
877{
878 if ( !mPickingColor )
879 {
880 //if not picking a color, use default tool button behavior
882 return;
883 }
884
885 //cancel picking, sampling the color if space was pressed
886 stopPicking( QCursor::pos(), e->key() == Qt::Key_Space );
887}
888
889
890void QgsCompoundColorWidget::updateComponent()
891{
892 const bool isCmyk = mColorModel->currentData().toInt() == QColor::Spec::Cmyk;
893 const auto radios = isCmyk ? mCmykRadios : mRgbRadios;
894 const QButtonGroup *group = isCmyk ? mCmykGroup : mRgbGroup;
895
896 const int id = group->checkedId();
897 if ( id >= 0 && id < radios.count() )
898 {
899 const QgsColorWidget::ColorComponent component = radios.at( group->checkedId() ).second;
900 mColorBox->setComponent( component );
901 mVerticalRamp->setComponent( component );
902 }
903}
904
905void QgsCompoundColorWidget::onColorButtonGroupToggled( int, bool checked )
906{
907 if ( checked )
908 updateComponent();
909}
910
911void QgsCompoundColorWidget::mAddColorToSchemeButton_clicked()
912{
913 mSchemeList->addColor( mColorPreview->color(), QgsSymbolLayerUtils::colorToName( mColorPreview->color() ) );
914}
915
916void QgsCompoundColorWidget::updateActionsForCurrentScheme()
917{
918 QgsColorScheme *scheme = mSchemeList->scheme();
919
920 mActionImportColors->setEnabled( scheme->isEditable() );
921 mActionPasteColors->setEnabled( scheme->isEditable() );
922 mAddColorToSchemeButton->setEnabled( scheme->isEditable() );
923 mRemoveColorsFromSchemeButton->setEnabled( scheme->isEditable() );
924
925 QgsUserColorScheme *userScheme = dynamic_cast<QgsUserColorScheme *>( scheme );
926 mActionRemovePalette->setEnabled( static_cast< bool >( userScheme ) );
927 if ( userScheme )
928 {
929 mActionShowInButtons->setEnabled( true );
930 whileBlocking( mActionShowInButtons )->setChecked( userScheme->flags() & QgsColorScheme::ShowInColorButtonMenu );
931 }
932 else
933 {
934 whileBlocking( mActionShowInButtons )->setChecked( false );
935 mActionShowInButtons->setEnabled( false );
936 }
937}
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:5568
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:5761