QGIS API Documentation 3.99.0-Master (21b3aa880ba)
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
18#include "qgsapplication.h"
19#include "qgscolorscheme.h"
21#include "qgsguiutils.h"
22#include "qgsscreenhelper.h"
23#include "qgssettings.h"
24#include "qgssymbollayerutils.h"
25
26#include <QButtonGroup>
27#include <QFileDialog>
28#include <QHeaderView>
29#include <QInputDialog>
30#include <QMenu>
31#include <QMessageBox>
32#include <QMouseEvent>
33#include <QPushButton>
34#include <QRegularExpression>
35#include <QScreen>
36#include <QToolButton>
37#include <QVBoxLayout>
38
39#include "moc_qgscompoundcolorwidget.cpp"
40
41QgsCompoundColorWidget::QgsCompoundColorWidget( QWidget *parent, const QColor &color, Layout widgetLayout )
42 : QgsPanelWidget( parent )
43{
44 setupUi( this );
45
46 mScreenHelper = new QgsScreenHelper( this );
47
48 mRgbRadios = {
55 };
56
57 mCmykRadios = {
62 };
63
64 mRgbGroup = new QButtonGroup( this );
65 int i = 0;
66 for ( auto colorRadio : mRgbRadios )
67 mRgbGroup->addButton( colorRadio.first, i++ );
68
69 mCmykGroup = new QButtonGroup( this );
70 i = 0;
71 for ( auto colorRadio : mCmykRadios )
72 mCmykGroup->addButton( colorRadio.first, i++ );
73
74 connect( mRgbGroup, &QButtonGroup::idToggled, this, &QgsCompoundColorWidget::onColorButtonGroupToggled );
75 connect( mCmykGroup, &QButtonGroup::idToggled, this, &QgsCompoundColorWidget::onColorButtonGroupToggled );
76 connect( mAddColorToSchemeButton, &QPushButton::clicked, this, &QgsCompoundColorWidget::mAddColorToSchemeButton_clicked );
77 connect( mAddCustomColorButton, &QPushButton::clicked, this, &QgsCompoundColorWidget::mAddCustomColorButton_clicked );
78 connect( mSampleButton, &QPushButton::clicked, this, &QgsCompoundColorWidget::mSampleButton_clicked );
79 connect( mTabWidget, &QTabWidget::currentChanged, this, &QgsCompoundColorWidget::mTabWidget_currentChanged );
80 connect( mActionShowInButtons, &QAction::toggled, this, &QgsCompoundColorWidget::mActionShowInButtons_toggled );
81
82 mColorModel->addItem( tr( "RGB" ), QColor::Spec::Rgb );
83 mColorModel->addItem( tr( "CMYK" ), QColor::Spec::Cmyk );
84 connect( mColorModel, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [this]( int ) {
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" ), tr( "Are you sure you want to remove %1?" ).arg( scheme->schemeName() ), 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:" ), QLineEdit::Normal, tr( "New palette" ), &ok );
477
478 if ( !ok || name.isEmpty() )
479 {
480 //user canceled
481 return nullptr;
482 }
483
484 //generate file name for new palette
485 const QDir palettePath( gplFilePath() );
486 const thread_local QRegularExpression badChars( "[,^@={}\\[\\]~!?:&*\"|#%<>$\"'();`' /\\\\]" );
487 QString filename = name.simplified().toLower().replace( badChars, QStringLiteral( "_" ) );
488 if ( filename.isEmpty() )
489 {
490 filename = tr( "new_palette" );
491 }
492 QFileInfo destFileInfo( palettePath.filePath( filename + ".gpl" ) );
493 int fileNumber = 1;
494 while ( destFileInfo.exists() )
495 {
496 //try to generate a unique file name
497 destFileInfo = QFileInfo( palettePath.filePath( filename + QStringLiteral( "%1.gpl" ).arg( fileNumber ) ) );
498 fileNumber++;
499 }
500
501 QgsUserColorScheme *newScheme = new QgsUserColorScheme( destFileInfo.fileName() );
502 newScheme->setName( name );
503
505 return newScheme;
506}
507
508void QgsCompoundColorWidget::newPalette()
509{
510 if ( createNewUserPalette( this ) )
511 {
512 //refresh combobox and set new scheme as active
513 refreshSchemeComboBox();
514 mSchemeComboBox->setCurrentIndex( mSchemeComboBox->count() - 1 );
515 }
516}
517
518QString QgsCompoundColorWidget::gplFilePath()
519{
520 QString palettesDir = QgsApplication::qgisSettingsDirPath() + "palettes";
521
522 const QDir localDir;
523 if ( !localDir.mkpath( palettesDir ) )
524 {
525 return QString();
526 }
527
528 return palettesDir;
529}
530
531void QgsCompoundColorWidget::schemeIndexChanged( int index )
532{
533 //save changes to scheme
534 if ( mSchemeList->isDirty() )
535 {
536 mSchemeList->saveColorsToScheme();
537 }
538
539 //get schemes with ShowInColorDialog set
540 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
541 if ( index >= schemeList.length() )
542 {
543 return;
544 }
545
546 QgsColorScheme *scheme = schemeList.at( index );
547 mSchemeList->setScheme( scheme );
548
549 updateActionsForCurrentScheme();
550
551 //copy action defaults to disabled
552 mActionCopyColors->setEnabled( false );
553}
554
555void QgsCompoundColorWidget::listSelectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
556{
557 Q_UNUSED( deselected )
558 mActionCopyColors->setEnabled( selected.length() > 0 );
559}
560
561void QgsCompoundColorWidget::mAddCustomColorButton_clicked()
562{
563 switch ( mLastCustomColorIndex )
564 {
565 case 0:
566 mSwatchButton1->setColor( mColorPreview->color() );
567 break;
568 case 1:
569 mSwatchButton2->setColor( mColorPreview->color() );
570 break;
571 case 2:
572 mSwatchButton3->setColor( mColorPreview->color() );
573 break;
574 case 3:
575 mSwatchButton4->setColor( mColorPreview->color() );
576 break;
577 case 4:
578 mSwatchButton5->setColor( mColorPreview->color() );
579 break;
580 case 5:
581 mSwatchButton6->setColor( mColorPreview->color() );
582 break;
583 case 6:
584 mSwatchButton7->setColor( mColorPreview->color() );
585 break;
586 case 7:
587 mSwatchButton8->setColor( mColorPreview->color() );
588 break;
589 case 8:
590 mSwatchButton9->setColor( mColorPreview->color() );
591 break;
592 case 9:
593 mSwatchButton10->setColor( mColorPreview->color() );
594 break;
595 case 10:
596 mSwatchButton11->setColor( mColorPreview->color() );
597 break;
598 case 11:
599 mSwatchButton12->setColor( mColorPreview->color() );
600 break;
601 case 12:
602 mSwatchButton13->setColor( mColorPreview->color() );
603 break;
604 case 13:
605 mSwatchButton14->setColor( mColorPreview->color() );
606 break;
607 case 14:
608 mSwatchButton15->setColor( mColorPreview->color() );
609 break;
610 case 15:
611 mSwatchButton16->setColor( mColorPreview->color() );
612 break;
613 }
614 mLastCustomColorIndex++;
615 if ( mLastCustomColorIndex >= 16 )
616 {
617 mLastCustomColorIndex = 0;
618 }
619}
620
621void QgsCompoundColorWidget::mSampleButton_clicked()
622{
623 //activate picker color
625 grabMouse();
626 grabKeyboard();
627 mPickingColor = true;
628 setMouseTracking( true );
629}
630
631void QgsCompoundColorWidget::mTabWidget_currentChanged( int index )
632{
633 //disable radio buttons if not using the first tab, as they have no meaning for other tabs
634 const bool enabled = index == 0;
635 const QList<QRadioButton *> colorRadios { mHueRadio, mSaturationRadio, mValueRadio, mRedRadio, mGreenRadio, mBlueRadio, mCyanRadio, mMagentaRadio, mYellowRadio, mBlackRadio };
636 for ( QRadioButton *colorRadio : colorRadios )
637 colorRadio->setEnabled( enabled );
638}
639
640void QgsCompoundColorWidget::mActionShowInButtons_toggled( bool state )
641{
642 QgsUserColorScheme *scheme = dynamic_cast<QgsUserColorScheme *>( mSchemeList->scheme() );
643 if ( scheme )
644 {
645 scheme->setShowSchemeInMenu( state );
646 }
647}
648
649QScreen *QgsCompoundColorWidget::findScreenAt( QPoint pos )
650{
651 const QList<QScreen *> screens = QGuiApplication::screens();
652 for ( QScreen *screen : screens )
653 {
654 if ( screen->geometry().contains( pos ) )
655 {
656 return screen;
657 }
658 }
659 return nullptr;
660}
661
662void QgsCompoundColorWidget::saveSettings()
663{
664 //save changes to scheme
665 if ( mSchemeList->isDirty() )
666 {
667 mSchemeList->saveColorsToScheme();
668 }
669
670 QgsSettings settings;
671
672 // record active component
673 settings.setValue( QStringLiteral( "Windows/ColorDialog/activeComponent" ), mRgbGroup->checkedId() );
674 settings.setValue( QStringLiteral( "Windows/ColorDialog/activeCmykComponent" ), mCmykGroup->checkedId() );
675
676 //record current scheme
677 settings.setValue( QStringLiteral( "Windows/ColorDialog/activeScheme" ), mSchemeComboBox->currentIndex() );
678
679 //record current tab
680 settings.setValue( QStringLiteral( "Windows/ColorDialog/activeTab" ), mTabWidget->currentIndex() );
681
682 //record custom colors
683 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor1" ), QVariant( mSwatchButton1->color() ) );
684 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor2" ), QVariant( mSwatchButton2->color() ) );
685 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor3" ), QVariant( mSwatchButton3->color() ) );
686 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor4" ), QVariant( mSwatchButton4->color() ) );
687 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor5" ), QVariant( mSwatchButton5->color() ) );
688 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor6" ), QVariant( mSwatchButton6->color() ) );
689 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor7" ), QVariant( mSwatchButton7->color() ) );
690 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor8" ), QVariant( mSwatchButton8->color() ) );
691 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor9" ), QVariant( mSwatchButton9->color() ) );
692 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor10" ), QVariant( mSwatchButton10->color() ) );
693 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor11" ), QVariant( mSwatchButton11->color() ) );
694 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor12" ), QVariant( mSwatchButton12->color() ) );
695 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor13" ), QVariant( mSwatchButton13->color() ) );
696 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor14" ), QVariant( mSwatchButton14->color() ) );
697 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor15" ), QVariant( mSwatchButton15->color() ) );
698 settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor16" ), QVariant( mSwatchButton16->color() ) );
699
700 //sample radius
701 settings.setValue( QStringLiteral( "Windows/ColorDialog/sampleRadius" ), mSpinBoxRadius->value() );
702}
703
704void QgsCompoundColorWidget::stopPicking( QPoint eventPos, const bool takeSample )
705{
706 //release mouse and keyboard, and reset cursor
707 releaseMouse();
708 releaseKeyboard();
709 unsetCursor();
710 setMouseTracking( false );
711 mPickingColor = false;
712
713 if ( !takeSample )
714 {
715 //not sampling color, nothing more to do
716 return;
717 }
718
719 //grab snapshot of pixel under mouse cursor
720 const QColor snappedColor = sampleColor( eventPos );
721 mSamplePreview->setColor( snappedColor );
722 mColorPreview->setColor( snappedColor, true );
723}
724
726{
727 const QColor::Spec colorSpec = color.spec() == QColor::Cmyk ? QColor::Cmyk : QColor::Rgb;
728 mColorModel->setCurrentIndex( mColorModel->findData( colorSpec ) );
729 mRGB->setVisible( colorSpec != QColor::Cmyk );
730 mCMYK->setVisible( colorSpec == QColor::Cmyk );
731 _setColor( color );
732}
733
734void QgsCompoundColorWidget::_setColor( const QColor &color )
735{
736 if ( !color.isValid() )
737 {
738 return;
739 }
740
741 QColor fixedColor = QColor( color );
742 if ( !mAllowAlpha )
743 {
744 //opacity disallowed, so don't permit transparent colors
745 fixedColor.setAlpha( 255 );
746 }
747
748 if ( mColorModel->currentIndex() && fixedColor.spec() != QColor::Cmyk )
749 {
750 fixedColor = fixedColor.toCmyk();
751 }
752
753 const QList<QgsColorWidget *> colorWidgets = this->findChildren<QgsColorWidget *>();
754 const auto constColorWidgets = colorWidgets;
755 for ( QgsColorWidget *widget : constColorWidgets )
756 {
757 if ( widget == mSamplePreview )
758 {
759 continue;
760 }
761 widget->blockSignals( true );
762 widget->setColor( fixedColor );
763 widget->blockSignals( false );
764 }
765
766
767 emit currentColorChanged( fixedColor );
768}
769
771{
772 mOldColorLabel->setVisible( color.isValid() );
773 mColorPreview->setColor2( color );
774}
775
777{
778 saveSettings();
779 QWidget::hideEvent( e );
780}
781
783{
784 if ( mPickingColor )
785 {
786 //don't show dialog if in color picker mode
787 e->accept();
788 return;
789 }
790
791 QWidget::mousePressEvent( e );
792}
793
794QColor QgsCompoundColorWidget::averageColor( const QImage &image ) const
795{
796 QRgb tmpRgb;
797 int colorCount = 0;
798 int sumRed = 0;
799 int sumBlue = 0;
800 int sumGreen = 0;
801 //scan through image and sum rgb components
802 for ( int heightIndex = 0; heightIndex < image.height(); ++heightIndex )
803 {
804 const QRgb *scanLine = reinterpret_cast<const QRgb *>( image.constScanLine( heightIndex ) );
805 for ( int widthIndex = 0; widthIndex < image.width(); ++widthIndex )
806 {
807 tmpRgb = scanLine[widthIndex];
808 sumRed += qRed( tmpRgb );
809 sumBlue += qBlue( tmpRgb );
810 sumGreen += qGreen( tmpRgb );
811 colorCount++;
812 }
813 }
814 //calculate average components as floats
815 const double avgRed = static_cast<double>( sumRed ) / ( 255.0 * colorCount );
816 const double avgGreen = static_cast<double>( sumGreen ) / ( 255.0 * colorCount );
817 const double avgBlue = static_cast<double>( sumBlue ) / ( 255.0 * colorCount );
818
819 //create a new color representing the average
820 return QColor::fromRgbF( avgRed, avgGreen, avgBlue );
821}
822
823QColor QgsCompoundColorWidget::sampleColor( QPoint point ) const
824{
825 const int sampleRadius = mSpinBoxRadius->value() - 1;
826 QScreen *screen = findScreenAt( point );
827 if ( !screen )
828 {
829 return QColor();
830 }
831
832 const int x = point.x() - screen->geometry().left();
833 const int y = point.y() - screen->geometry().top();
834 const QPixmap snappedPixmap = screen->grabWindow( 0, x - sampleRadius, y - sampleRadius, 1 + sampleRadius * 2, 1 + sampleRadius * 2 );
835 const QImage snappedImage = snappedPixmap.toImage();
836 //scan all pixels and take average color
837 return averageColor( snappedImage );
838}
839
841{
842 if ( mPickingColor )
843 {
844 //currently in color picker mode
845 //sample color under cursor update preview widget to give feedback to user
846 const QColor hoverColor = sampleColor( e->globalPos() );
847 mSamplePreview->setColor( hoverColor );
848
849 e->accept();
850 return;
851 }
852
853 QWidget::mouseMoveEvent( e );
854}
855
857{
858 if ( mPickingColor )
859 {
860 //end color picking operation by sampling the color under cursor
861 stopPicking( e->globalPos() );
862 e->accept();
863 return;
864 }
865
866 QWidget::mouseReleaseEvent( e );
867}
868
870{
871 if ( !mPickingColor )
872 {
873 //if not picking a color, use default tool button behavior
875 return;
876 }
877
878 //cancel picking, sampling the color if space was pressed
879 stopPicking( QCursor::pos(), e->key() == Qt::Key_Space );
880}
881
882
883void QgsCompoundColorWidget::updateComponent()
884{
885 const bool isCmyk = mColorModel->currentData().toInt() == QColor::Spec::Cmyk;
886 const auto radios = isCmyk ? mCmykRadios : mRgbRadios;
887 const QButtonGroup *group = isCmyk ? mCmykGroup : mRgbGroup;
888
889 const int id = group->checkedId();
890 if ( id >= 0 && id < radios.count() )
891 {
892 const QgsColorWidget::ColorComponent component = radios.at( group->checkedId() ).second;
893 mColorBox->setComponent( component );
894 mVerticalRamp->setComponent( component );
895 }
896}
897
898void QgsCompoundColorWidget::onColorButtonGroupToggled( int, bool checked )
899{
900 if ( checked )
901 updateComponent();
902}
903
904void QgsCompoundColorWidget::mAddColorToSchemeButton_clicked()
905{
906 mSchemeList->addColor( mColorPreview->color(), QgsSymbolLayerUtils::colorToName( mColorPreview->color() ) );
907}
908
909void QgsCompoundColorWidget::updateActionsForCurrentScheme()
910{
911 QgsColorScheme *scheme = mSchemeList->scheme();
912
913 mActionImportColors->setEnabled( scheme->isEditable() );
914 mActionPasteColors->setEnabled( scheme->isEditable() );
915 mAddColorToSchemeButton->setEnabled( scheme->isEditable() );
916 mRemoveColorsFromSchemeButton->setEnabled( scheme->isEditable() );
917
918 QgsUserColorScheme *userScheme = dynamic_cast<QgsUserColorScheme *>( scheme );
919 mActionRemovePalette->setEnabled( static_cast<bool>( userScheme ) );
920 if ( userScheme )
921 {
922 mActionShowInButtons->setEnabled( true );
923 whileBlocking( mActionShowInButtons )->setChecked( userScheme->flags() & QgsColorScheme::ShowInColorButtonMenu );
924 }
925 else
926 {
927 whileBlocking( mActionShowInButtons )->setChecked( false );
928 mActionShowInButtons->setEnabled( false );
929 }
930}
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:6222
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.
@ 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.
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.
QgsPanelWidget(QWidget *parent=nullptr)
Base class for any widget that can be shown as an 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.
Stores settings for use within QGIS.
Definition qgssettings.h:65
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:6511