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