QGIS API Documentation 4.1.0-Master (60fea48833c)
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 <QString>
37#include <QToolButton>
38#include <QVBoxLayout>
39
40#include "moc_qgscompoundcolorwidget.cpp"
41
42using namespace Qt::StringLiterals;
43
44QgsCompoundColorWidget::QgsCompoundColorWidget( QWidget *parent, const QColor &color, Layout widgetLayout )
45 : QgsPanelWidget( parent )
46{
47 setupUi( this );
48
49 mScreenHelper = new QgsScreenHelper( this );
50
51 mRgbRadios = {
58 };
59
60 mCmykRadios
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 const QColor::Spec spec = static_cast<QColor::Spec>( mColorModel->currentData().toInt() );
85 if ( spec == QColor::Spec::Cmyk )
86 setColor( this->color().toCmyk() );
87 else
88 setColor( this->color().toRgb() );
89
90 updateComponent();
91 } );
92
93 if ( widgetLayout == LayoutVertical )
94 {
95 // shuffle stuff around
96 QVBoxLayout *newLayout = new QVBoxLayout();
97 newLayout->setContentsMargins( 0, 0, 0, 0 );
98 newLayout->addWidget( mTabWidget );
99 newLayout->addWidget( mSlidersWidget );
100 newLayout->addWidget( mPreviewWidget );
101 newLayout->addWidget( mSwatchesWidget );
102 delete layout();
103 setLayout( newLayout );
104 }
105
106 const QgsSettings settings;
107
108 mSchemeList->header()->hide();
109 mSchemeList->setColumnWidth( 0, static_cast<int>( Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 6 ) );
110
111 //get schemes with ShowInColorDialog set
112 refreshSchemeComboBox();
113 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
114
115 //choose a reasonable starting scheme
116 int activeScheme = settings.value( u"Windows/ColorDialog/activeScheme"_s, 0 ).toInt();
117 activeScheme = activeScheme >= mSchemeComboBox->count() ? 0 : activeScheme;
118
119 mSchemeList->setScheme( schemeList.at( activeScheme ) );
120
121 mSchemeComboBox->setCurrentIndex( activeScheme );
122 updateActionsForCurrentScheme();
123
124 //listen out for selection changes in list, so we can enable/disable the copy colors option
125 connect( mSchemeList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsCompoundColorWidget::listSelectionChanged );
126 //copy action defaults to disabled
127 mActionCopyColors->setEnabled( false );
128
129 connect( mActionCopyColors, &QAction::triggered, mSchemeList, &QgsColorSchemeList::copyColors );
130 connect( mActionPasteColors, &QAction::triggered, mSchemeList, &QgsColorSchemeList::pasteColors );
131 connect( mActionExportColors, &QAction::triggered, mSchemeList, &QgsColorSchemeList::showExportColorsDialog );
132 connect( mActionImportColors, &QAction::triggered, mSchemeList, &QgsColorSchemeList::showImportColorsDialog );
133 connect( mActionImportPalette, &QAction::triggered, this, &QgsCompoundColorWidget::importPalette );
134 connect( mActionRemovePalette, &QAction::triggered, this, &QgsCompoundColorWidget::removePalette );
135 connect( mActionNewPalette, &QAction::triggered, this, &QgsCompoundColorWidget::newPalette );
136 connect( mRemoveColorsFromSchemeButton, &QAbstractButton::clicked, mSchemeList, &QgsColorSchemeList::removeSelection );
137
138 QMenu *schemeMenu = new QMenu( mSchemeToolButton );
139 schemeMenu->addAction( mActionCopyColors );
140 schemeMenu->addAction( mActionPasteColors );
141 schemeMenu->addSeparator();
142 schemeMenu->addAction( mActionImportColors );
143 schemeMenu->addAction( mActionExportColors );
144 schemeMenu->addSeparator();
145 schemeMenu->addAction( mActionNewPalette );
146 schemeMenu->addAction( mActionImportPalette );
147 schemeMenu->addAction( mActionRemovePalette );
148 schemeMenu->addAction( mActionShowInButtons );
149 mSchemeToolButton->setMenu( schemeMenu );
150
151 connect( mSchemeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsCompoundColorWidget::schemeIndexChanged );
152 connect( mSchemeList, &QgsColorSchemeList::colorSelected, this, &QgsCompoundColorWidget::_setColor );
153
154 mOldColorLabel->hide();
155
156 mVerticalRamp->setOrientation( QgsColorRampWidget::Vertical );
157 mVerticalRamp->setInteriorMargin( 2 );
158 mVerticalRamp->setShowFrame( true );
159
160 mRedSlider->setComponent( QgsColorWidget::Red );
161 mGreenSlider->setComponent( QgsColorWidget::Green );
162 mBlueSlider->setComponent( QgsColorWidget::Blue );
163 mHueSlider->setComponent( QgsColorWidget::Hue );
164 mSaturationSlider->setComponent( QgsColorWidget::Saturation );
165 mValueSlider->setComponent( QgsColorWidget::Value );
166 mAlphaSlider->setComponent( QgsColorWidget::Alpha );
167 mCyanSlider->setComponent( QgsColorWidget::Cyan );
168 mMagentaSlider->setComponent( QgsColorWidget::Magenta );
169 mYellowSlider->setComponent( QgsColorWidget::Yellow );
170 mBlackSlider->setComponent( QgsColorWidget::Black );
171
172 mSwatchButton1->setShowMenu( false );
173 mSwatchButton1->setBehavior( QgsColorButton::SignalOnly );
174 mSwatchButton2->setShowMenu( false );
175 mSwatchButton2->setBehavior( QgsColorButton::SignalOnly );
176 mSwatchButton3->setShowMenu( false );
177 mSwatchButton3->setBehavior( QgsColorButton::SignalOnly );
178 mSwatchButton4->setShowMenu( false );
179 mSwatchButton4->setBehavior( QgsColorButton::SignalOnly );
180 mSwatchButton5->setShowMenu( false );
181 mSwatchButton5->setBehavior( QgsColorButton::SignalOnly );
182 mSwatchButton6->setShowMenu( false );
183 mSwatchButton6->setBehavior( QgsColorButton::SignalOnly );
184 mSwatchButton7->setShowMenu( false );
185 mSwatchButton7->setBehavior( QgsColorButton::SignalOnly );
186 mSwatchButton8->setShowMenu( false );
187 mSwatchButton8->setBehavior( QgsColorButton::SignalOnly );
188 mSwatchButton9->setShowMenu( false );
189 mSwatchButton9->setBehavior( QgsColorButton::SignalOnly );
190 mSwatchButton10->setShowMenu( false );
191 mSwatchButton10->setBehavior( QgsColorButton::SignalOnly );
192 mSwatchButton11->setShowMenu( false );
193 mSwatchButton11->setBehavior( QgsColorButton::SignalOnly );
194 mSwatchButton12->setShowMenu( false );
195 mSwatchButton12->setBehavior( QgsColorButton::SignalOnly );
196 mSwatchButton13->setShowMenu( false );
197 mSwatchButton13->setBehavior( QgsColorButton::SignalOnly );
198 mSwatchButton14->setShowMenu( false );
199 mSwatchButton14->setBehavior( QgsColorButton::SignalOnly );
200 mSwatchButton15->setShowMenu( false );
201 mSwatchButton15->setBehavior( QgsColorButton::SignalOnly );
202 mSwatchButton16->setShowMenu( false );
203 mSwatchButton16->setBehavior( QgsColorButton::SignalOnly );
204 //restore custom colors
205 mSwatchButton1->setColor( settings.value( u"Windows/ColorDialog/customColor1"_s, QVariant( QColor() ) ).value<QColor>() );
206 mSwatchButton2->setColor( settings.value( u"Windows/ColorDialog/customColor2"_s, QVariant( QColor() ) ).value<QColor>() );
207 mSwatchButton3->setColor( settings.value( u"Windows/ColorDialog/customColor3"_s, QVariant( QColor() ) ).value<QColor>() );
208 mSwatchButton4->setColor( settings.value( u"Windows/ColorDialog/customColor4"_s, QVariant( QColor() ) ).value<QColor>() );
209 mSwatchButton5->setColor( settings.value( u"Windows/ColorDialog/customColor5"_s, QVariant( QColor() ) ).value<QColor>() );
210 mSwatchButton6->setColor( settings.value( u"Windows/ColorDialog/customColor6"_s, QVariant( QColor() ) ).value<QColor>() );
211 mSwatchButton7->setColor( settings.value( u"Windows/ColorDialog/customColor7"_s, QVariant( QColor() ) ).value<QColor>() );
212 mSwatchButton8->setColor( settings.value( u"Windows/ColorDialog/customColor8"_s, QVariant( QColor() ) ).value<QColor>() );
213 mSwatchButton9->setColor( settings.value( u"Windows/ColorDialog/customColor9"_s, QVariant( QColor() ) ).value<QColor>() );
214 mSwatchButton10->setColor( settings.value( u"Windows/ColorDialog/customColor10"_s, QVariant( QColor() ) ).value<QColor>() );
215 mSwatchButton11->setColor( settings.value( u"Windows/ColorDialog/customColor11"_s, QVariant( QColor() ) ).value<QColor>() );
216 mSwatchButton12->setColor( settings.value( u"Windows/ColorDialog/customColor12"_s, QVariant( QColor() ) ).value<QColor>() );
217 mSwatchButton13->setColor( settings.value( u"Windows/ColorDialog/customColor13"_s, QVariant( QColor() ) ).value<QColor>() );
218 mSwatchButton14->setColor( settings.value( u"Windows/ColorDialog/customColor14"_s, QVariant( QColor() ) ).value<QColor>() );
219 mSwatchButton15->setColor( settings.value( u"Windows/ColorDialog/customColor15"_s, QVariant( QColor() ) ).value<QColor>() );
220 mSwatchButton16->setColor( settings.value( u"Windows/ColorDialog/customColor16"_s, QVariant( QColor() ) ).value<QColor>() );
221
222 //restore sample radius
223 mSpinBoxRadius->setValue( settings.value( u"Windows/ColorDialog/sampleRadius"_s, 1 ).toInt() );
224 mSamplePreview->setColor( QColor() );
225
226 // hidpi friendly sizes
227 const int swatchWidth = static_cast<int>( std::round( std::max( Qgis::UI_SCALE_FACTOR * 1.9 * mSwatchButton1->fontMetrics().height(), 38.0 ) ) );
228 const int swatchHeight = static_cast<int>( std::round( std::max( Qgis::UI_SCALE_FACTOR * 1.5 * mSwatchButton1->fontMetrics().height(), 30.0 ) ) );
229 mSwatchButton1->setMinimumSize( swatchWidth, swatchHeight );
230 mSwatchButton1->setMaximumSize( swatchWidth, swatchHeight );
231 mSwatchButton2->setMinimumSize( swatchWidth, swatchHeight );
232 mSwatchButton2->setMaximumSize( swatchWidth, swatchHeight );
233 mSwatchButton3->setMinimumSize( swatchWidth, swatchHeight );
234 mSwatchButton3->setMaximumSize( swatchWidth, swatchHeight );
235 mSwatchButton4->setMinimumSize( swatchWidth, swatchHeight );
236 mSwatchButton4->setMaximumSize( swatchWidth, swatchHeight );
237 mSwatchButton5->setMinimumSize( swatchWidth, swatchHeight );
238 mSwatchButton5->setMaximumSize( swatchWidth, swatchHeight );
239 mSwatchButton6->setMinimumSize( swatchWidth, swatchHeight );
240 mSwatchButton6->setMaximumSize( swatchWidth, swatchHeight );
241 mSwatchButton7->setMinimumSize( swatchWidth, swatchHeight );
242 mSwatchButton7->setMaximumSize( swatchWidth, swatchHeight );
243 mSwatchButton8->setMinimumSize( swatchWidth, swatchHeight );
244 mSwatchButton8->setMaximumSize( swatchWidth, swatchHeight );
245 mSwatchButton9->setMinimumSize( swatchWidth, swatchHeight );
246 mSwatchButton9->setMaximumSize( swatchWidth, swatchHeight );
247 mSwatchButton10->setMinimumSize( swatchWidth, swatchHeight );
248 mSwatchButton10->setMaximumSize( swatchWidth, swatchHeight );
249 mSwatchButton11->setMinimumSize( swatchWidth, swatchHeight );
250 mSwatchButton11->setMaximumSize( swatchWidth, swatchHeight );
251 mSwatchButton12->setMinimumSize( swatchWidth, swatchHeight );
252 mSwatchButton12->setMaximumSize( swatchWidth, swatchHeight );
253 mSwatchButton13->setMinimumSize( swatchWidth, swatchHeight );
254 mSwatchButton13->setMaximumSize( swatchWidth, swatchHeight );
255 mSwatchButton14->setMinimumSize( swatchWidth, swatchHeight );
256 mSwatchButton14->setMaximumSize( swatchWidth, swatchHeight );
257 mSwatchButton15->setMinimumSize( swatchWidth, swatchHeight );
258 mSwatchButton15->setMaximumSize( swatchWidth, swatchHeight );
259 mSwatchButton16->setMinimumSize( swatchWidth, swatchHeight );
260 mSwatchButton16->setMaximumSize( swatchWidth, swatchHeight );
261 const int previewHeight = static_cast<int>( std::round( std::max( Qgis::UI_SCALE_FACTOR * 2.0 * mSwatchButton1->fontMetrics().height(), 40.0 ) ) );
262 mColorPreview->setMinimumSize( 0, previewHeight );
263 mPreviewWidget->setMaximumHeight( previewHeight * 2 );
264 const int swatchAddSize = static_cast<int>( std::round( std::max( Qgis::UI_SCALE_FACTOR * 1.4 * mSwatchButton1->fontMetrics().height(), 28.0 ) ) );
265 mAddCustomColorButton->setMinimumWidth( swatchAddSize );
266 mAddCustomColorButton->setMaximumWidth( swatchAddSize );
267
268 const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
269 mTabWidget->setIconSize( QSize( iconSize, iconSize ) );
270
271 setColor( color );
272
273 // restore active Rgb/Cmyk component radio button
274 const int activeRgbRadio = settings.value( u"Windows/ColorDialog/activeComponent"_s, 2 ).toInt();
275 if ( QAbstractButton *rgbRadio = mRgbGroup->button( activeRgbRadio ) )
276 rgbRadio->setChecked( true );
277
278 const int activeCmykRadio = settings.value( u"Windows/ColorDialog/activeCmykComponent"_s, 0 ).toInt();
279 if ( QAbstractButton *cmykRadio = mCmykGroup->button( activeCmykRadio ) )
280 cmykRadio->setChecked( true );
281
282 const int currentTab = settings.value( u"Windows/ColorDialog/activeTab"_s, 0 ).toInt();
283 mTabWidget->setCurrentIndex( currentTab );
284
285 //setup connections
286 connect( mColorBox, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
287 connect( mColorWheel, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
288 connect( mColorText, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
289 connect( mVerticalRamp, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
290 connect( mRedSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
291 connect( mGreenSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
292 connect( mBlueSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
293 connect( mHueSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
294 connect( mValueSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
295 connect( mCyanSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
296 connect( mMagentaSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
297 connect( mYellowSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
298 connect( mBlackSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
299 connect( mSaturationSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
300 connect( mAlphaSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
301 connect( mColorPreview, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::_setColor );
302 connect( mSwatchButton1, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
303 connect( mSwatchButton2, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
304 connect( mSwatchButton3, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
305 connect( mSwatchButton4, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
306 connect( mSwatchButton5, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
307 connect( mSwatchButton6, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
308 connect( mSwatchButton7, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
309 connect( mSwatchButton8, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
310 connect( mSwatchButton9, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
311 connect( mSwatchButton10, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
312 connect( mSwatchButton11, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
313 connect( mSwatchButton12, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
314 connect( mSwatchButton13, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
315 connect( mSwatchButton14, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
316 connect( mSwatchButton15, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
317 connect( mSwatchButton16, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::_setColor );
318}
319
327
329{
330 //all widgets should have the same color, so it shouldn't matter
331 //which we fetch it from
332 return mColorPreview->color();
333}
334
335void QgsCompoundColorWidget::setAllowOpacity( const bool allowOpacity )
336{
337 mAllowAlpha = allowOpacity;
338 mAlphaLabel->setVisible( allowOpacity );
339 mAlphaSlider->setVisible( allowOpacity );
340 mColorText->setAllowOpacity( allowOpacity );
341 if ( !allowOpacity )
342 {
343 mAlphaLayout->setContentsMargins( 0, 0, 0, 0 );
344 mAlphaLayout->setSpacing( 0 );
345 }
346}
347
349{
350 mColorModel->setVisible( colorModelEditable );
351}
352
353void QgsCompoundColorWidget::refreshSchemeComboBox()
354{
355 mSchemeComboBox->blockSignals( true );
356 mSchemeComboBox->clear();
357 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
358 QList<QgsColorScheme *>::const_iterator schemeIt = schemeList.constBegin();
359 for ( ; schemeIt != schemeList.constEnd(); ++schemeIt )
360 {
361 mSchemeComboBox->addItem( ( *schemeIt )->schemeName() );
362 }
363 mSchemeComboBox->blockSignals( false );
364}
365
366
368{
369 QgsSettings s;
370 const QString lastDir = s.value( u"/UI/lastGplPaletteDir"_s, QDir::homePath() ).toString();
371 const QString filePath = QFileDialog::getOpenFileName( parent, tr( "Select Palette File" ), lastDir, u"GPL (*.gpl);;All files (*.*)"_s );
372 if ( parent )
373 parent->activateWindow();
374 if ( filePath.isEmpty() )
375 {
376 return nullptr;
377 }
378
379 //check if file exists
380 const QFileInfo fileInfo( filePath );
381 if ( !fileInfo.exists() || !fileInfo.isReadable() )
382 {
383 QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "Error, file does not exist or is not readable." ) );
384 return nullptr;
385 }
386
387 s.setValue( u"/UI/lastGplPaletteDir"_s, fileInfo.absolutePath() );
388 QFile file( filePath );
389
390 QgsNamedColorList importedColors;
391 bool ok = false;
392 QString paletteName;
393 importedColors = QgsSymbolLayerUtils::importColorsFromGpl( file, ok, paletteName );
394 if ( !ok )
395 {
396 QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "Palette file is not readable." ) );
397 return nullptr;
398 }
399
400 if ( importedColors.length() == 0 )
401 {
402 //no imported colors
403 QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "No colors found in palette file." ) );
404 return nullptr;
405 }
406
407 //TODO - handle conflicting file names, name for new palette
408 QgsUserColorScheme *importedScheme = new QgsUserColorScheme( fileInfo.fileName() );
409 importedScheme->setName( paletteName );
410 importedScheme->setColors( importedColors );
411
413 return importedScheme;
414}
415
416void QgsCompoundColorWidget::importPalette()
417{
418 if ( importUserPaletteFromFile( this ) )
419 {
420 //refresh combobox
421 refreshSchemeComboBox();
422 mSchemeComboBox->setCurrentIndex( mSchemeComboBox->count() - 1 );
423 }
424}
425
426
428{
429 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 )
430 != 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, u"_"_s );
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 + u"%1.gpl"_s.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( u"Windows/ColorDialog/activeComponent"_s, mRgbGroup->checkedId() );
674 settings.setValue( u"Windows/ColorDialog/activeCmykComponent"_s, mCmykGroup->checkedId() );
675
676 //record current scheme
677 settings.setValue( u"Windows/ColorDialog/activeScheme"_s, mSchemeComboBox->currentIndex() );
678
679 //record current tab
680 settings.setValue( u"Windows/ColorDialog/activeTab"_s, mTabWidget->currentIndex() );
681
682 //record custom colors
683 settings.setValue( u"Windows/ColorDialog/customColor1"_s, QVariant( mSwatchButton1->color() ) );
684 settings.setValue( u"Windows/ColorDialog/customColor2"_s, QVariant( mSwatchButton2->color() ) );
685 settings.setValue( u"Windows/ColorDialog/customColor3"_s, QVariant( mSwatchButton3->color() ) );
686 settings.setValue( u"Windows/ColorDialog/customColor4"_s, QVariant( mSwatchButton4->color() ) );
687 settings.setValue( u"Windows/ColorDialog/customColor5"_s, QVariant( mSwatchButton5->color() ) );
688 settings.setValue( u"Windows/ColorDialog/customColor6"_s, QVariant( mSwatchButton6->color() ) );
689 settings.setValue( u"Windows/ColorDialog/customColor7"_s, QVariant( mSwatchButton7->color() ) );
690 settings.setValue( u"Windows/ColorDialog/customColor8"_s, QVariant( mSwatchButton8->color() ) );
691 settings.setValue( u"Windows/ColorDialog/customColor9"_s, QVariant( mSwatchButton9->color() ) );
692 settings.setValue( u"Windows/ColorDialog/customColor10"_s, QVariant( mSwatchButton10->color() ) );
693 settings.setValue( u"Windows/ColorDialog/customColor11"_s, QVariant( mSwatchButton11->color() ) );
694 settings.setValue( u"Windows/ColorDialog/customColor12"_s, QVariant( mSwatchButton12->color() ) );
695 settings.setValue( u"Windows/ColorDialog/customColor13"_s, QVariant( mSwatchButton13->color() ) );
696 settings.setValue( u"Windows/ColorDialog/customColor14"_s, QVariant( mSwatchButton14->color() ) );
697 settings.setValue( u"Windows/ColorDialog/customColor15"_s, QVariant( mSwatchButton15->color() ) );
698 settings.setValue( u"Windows/ColorDialog/customColor16"_s, QVariant( mSwatchButton16->color() ) );
699
700 //sample radius
701 settings.setValue( u"Windows/ColorDialog/sampleRadius"_s, 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:6591
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:68
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:6880