From 24437d130900594277c8b171dd857e7ebe8013b0 Mon Sep 17 00:00:00 2001 From: Julien Cabieces Date: Thu, 30 May 2024 15:52:14 +0200 Subject: [PATCH 1/8] feat(CMYK): Add project properties configuration widgets --- src/app/qgsprojectproperties.cpp | 64 ++++++++++ src/app/qgsprojectproperties.h | 30 +++++ src/ui/qgsprojectpropertiesbase.ui | 138 ++++++++++++++------- tests/src/app/testqgsprojectproperties.cpp | 40 ++++++ 4 files changed, 224 insertions(+), 48 deletions(-) diff --git a/src/app/qgsprojectproperties.cpp b/src/app/qgsprojectproperties.cpp index b72df719d596..2b975a4c2018 100644 --- a/src/app/qgsprojectproperties.cpp +++ b/src/app/qgsprojectproperties.cpp @@ -22,6 +22,7 @@ #include "qgsapplication.h" #include "qgisapp.h" #include "qgis.h" +#include "qgscolorutils.h" #include "qgscoordinatetransform.h" #include "qgsdatumtransformtablewidget.h" #include "qgslayoutmanager.h" @@ -134,6 +135,10 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa connect( mButtonNewStyleDatabase, &QAbstractButton::clicked, this, &QgsProjectProperties::newStyleDatabase ); connect( mCoordinateDisplayComboBox, qOverload( &QComboBox::currentIndexChanged ), this, [ = ]( int ) { updateGuiForCoordinateType(); } ); connect( mCoordinateCrs, &QgsProjectionSelectionWidget::crsChanged, this, [ = ]( const QgsCoordinateReferenceSystem & ) { updateGuiForCoordinateCrs(); } ); +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + connect( mAddIccProfile, &QToolButton::clicked, this, static_cast( &QgsProjectProperties::addIccProfile ) ); + connect( mRemoveIccProfile, &QToolButton::clicked, this, &QgsProjectProperties::removeIccProfile ); +#endif // QgsOptionsDialogBase handles saving/restoring of geometry, splitter and current tab states, // switching vertical tabs between icon/text to icon-only modes (splitter collapsed to left), @@ -1025,6 +1030,19 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa // Random colors cbxStyleRandomColors->setChecked( QgsProject::instance()->styleSettings()->randomizeDefaultSymbolColor() ); + mColorModel->addItem( tr( "RGB" ), QVariant::fromValue( Qgis::ColorModel::Rgb ) ); + mColorModel->addItem( tr( "CMYK" ), QVariant::fromValue( Qgis::ColorModel::Cmyk ) ); + mColorModel->setCurrentIndex( mColorModel->findData( QVariant::fromValue( QgsProject::instance()->styleSettings()->colorModel() ) ) ); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + mColorSpace = QgsProject::instance()->styleSettings()->colorSpace(); + updateColorSpaceWidget(); +#else + mIccProfileLabel->setVisible( false ); + mColorSpaceName->setVisible( false ); + mAddIccProfile->setVisible( false ); + mRemoveIccProfile->setVisible( false ); +#endif // Default alpha transparency mDefaultOpacityWidget->setOpacity( QgsProject::instance()->styleSettings()->defaultSymbolOpacity() ); @@ -1733,6 +1751,8 @@ void QgsProjectProperties::apply() QgsProject::instance()->styleSettings()->setDefaultTextFormat( mStyleTextFormat->textFormat() ); QgsProject::instance()->styleSettings()->setRandomizeDefaultSymbolColor( cbxStyleRandomColors->isChecked() ); QgsProject::instance()->styleSettings()->setDefaultSymbolOpacity( mDefaultOpacityWidget->opacity() ); + QgsProject::instance()->styleSettings()->setColorModel( mColorModel->currentData().value() ); + QgsProject::instance()->styleSettings()->setColorSpace( mColorSpace ); { QStringList styleDatabasePaths; @@ -2680,6 +2700,50 @@ void QgsProjectProperties::removeStyleDatabase() delete mListStyleDatabases->takeItem( currentRow ); } +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + +void QgsProjectProperties::addIccProfile() +{ + const QString iccProfileFilePath = QFileDialog::getOpenFileName( + this, + tr( "Load ICC Profile" ), + QDir::homePath(), + tr( "Style databases" ) + tr( "ICC Profile" ) + QStringLiteral( " (*.icc)" ) ); + + addIccProfile( iccProfileFilePath ); +} + +void QgsProjectProperties::addIccProfile( const QString &iccProfileFilePath ) +{ + if ( iccProfileFilePath.isEmpty() ) + return; + + QString errorMsg; + QColorSpace colorSpace = QgsColorUtils::iccProfile( iccProfileFilePath, errorMsg ); + if ( !colorSpace.isValid() ) + { + QMessageBox::warning( this, tr( "Load ICC Profile" ), errorMsg ); + return; + } + + mColorSpace = colorSpace; + updateColorSpaceWidget(); +} + +void QgsProjectProperties::removeIccProfile() +{ + mColorSpace = QColorSpace(); + updateColorSpaceWidget(); +} + +void QgsProjectProperties::updateColorSpaceWidget() +{ + mColorSpaceName->setText( mColorSpace.isValid() ? mColorSpace.description() : tr( "None" ) ); + mRemoveIccProfile->setEnabled( mColorSpace.isValid() ); +} + +#endif + QListWidgetItem *QgsProjectProperties::addScaleToScaleList( const double newScaleDenominator ) { // TODO QGIS3: Rework the scale list widget to be a reusable piece of code, see PR #2558 diff --git a/src/app/qgsprojectproperties.h b/src/app/qgsprojectproperties.h index 5c092c74b2f5..5bd992828c6b 100644 --- a/src/app/qgsprojectproperties.h +++ b/src/app/qgsprojectproperties.h @@ -29,6 +29,7 @@ #include "qgis_app.h" #include +#include class QgsMapCanvas; class QgsRelationManagerDialog; @@ -204,6 +205,34 @@ class APP_EXPORT QgsProjectProperties : public QgsOptionsDialogBase, private Ui: void removeStyleDatabase(); void newStyleDatabase(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + + /** + * Called whenever user select the add ICC profile button + * \since QGIS 3.40 + */ + void addIccProfile(); + + /** + * load \a iccProfileFilePath and set resulting color space to project + * \since QGIS 3.40 + */ + void addIccProfile( const QString &iccProfileFilePath ); + + /** + * Called whenever user select the remove ICC profile button + * \since QGIS 3.40 + */ + void removeIccProfile(); + + /** + * Update color space widget according to current project color space + * \since QGIS 3.40 + */ + void updateColorSpaceWidget(); + +#endif + private: /** @@ -240,6 +269,7 @@ class APP_EXPORT QgsProjectProperties : public QgsOptionsDialogBase, private Ui: QList mEllipsoidList; int mEllipsoidIndex; bool mBlockCrsUpdates = false; + QColorSpace mColorSpace; QList< QgsOptionsPageWidget * > mAdditionalProjectPropertiesWidgets; diff --git a/src/ui/qgsprojectpropertiesbase.ui b/src/ui/qgsprojectpropertiesbase.ui index da4b036e333e..75983d60106d 100644 --- a/src/ui/qgsprojectpropertiesbase.ui +++ b/src/ui/qgsprojectpropertiesbase.ui @@ -269,7 +269,7 @@ - 10 + 5 @@ -298,8 +298,8 @@ 0 0 - 640 - 971 + 673 + 852 @@ -317,7 +317,7 @@ General Settings - + projgeneral @@ -578,7 +578,7 @@ Measurements - + projgeneral @@ -647,7 +647,7 @@ Coordinate and Bearing Display - + projgeneral @@ -797,7 +797,7 @@ - + false @@ -901,7 +901,7 @@ false - + projgeneral @@ -1042,8 +1042,8 @@ 0 0 - 340 - 57 + 279 + 52 @@ -1107,8 +1107,8 @@ 0 0 - 634 - 90 + 523 + 80 @@ -1179,8 +1179,8 @@ 0 0 - 344 - 817 + 673 + 846 @@ -1201,7 +1201,7 @@ Default Symbols - + projstyles @@ -1219,7 +1219,7 @@ - + 0 @@ -1353,7 +1353,7 @@ - + 0 @@ -1393,35 +1393,77 @@ Options - + projstyles - - - - - - - Opacity - - - - - - - Qt::StrongFocus - - - - - - + + Assign random colors to symbols + + + + Color model + + + + + + + ... + + + + :/images/themes/default/mActionDeleteSelected.svg:/images/themes/default/mActionDeleteSelected.svg + + + + + + + + + + + + + + Opacity + + + + + + + ICC Profile + + + + + + + + + + + :/images/themes/default/mActionFileOpen.svg:/images/themes/default/mActionFileOpen.svg + + + + + + + + + + Qt::StrongFocus + + + @@ -1554,7 +1596,7 @@ false - + projgeneral @@ -1852,8 +1894,8 @@ 0 0 - 176 - 45 + 151 + 42 @@ -1935,8 +1977,8 @@ 0 0 - 679 - 1515 + 673 + 1503 @@ -1990,13 +2032,13 @@ false - + false - + projowsserver - + true @@ -2692,10 +2734,10 @@ false - + false - + true diff --git a/tests/src/app/testqgsprojectproperties.cpp b/tests/src/app/testqgsprojectproperties.cpp index f2eede7fae91..4115f41e3849 100644 --- a/tests/src/app/testqgsprojectproperties.cpp +++ b/tests/src/app/testqgsprojectproperties.cpp @@ -24,6 +24,7 @@ #include "qgsbearingnumericformat.h" #include "qgsrasterlayer.h" #include "qgsprojecttimesettings.h" +#include "qgsprojectstylesettings.h" #include "qgsmaplayertemporalproperties.h" #include "qgsrasterlayertemporalproperties.h" @@ -48,6 +49,7 @@ class TestQgsProjectProperties : public QObject void testEllipsoidCrsSync(); void testBearingFormat(); void testTimeSettings(); + void testColorSettings(); private: QgisApp *mQgisApp = nullptr; @@ -255,6 +257,44 @@ void TestQgsProjectProperties::testTimeSettings() QCOMPARE( secondProjectRange, expectedRange ); } +void TestQgsProjectProperties::testColorSettings() +{ + QgsProject::instance()->clear(); + QCOMPARE( QgsProject::instance()->styleSettings()->colorModel(), Qgis::ColorModel::Rgb ); + QVERIFY( !QgsProject::instance()->styleSettings()->colorSpace().isValid() ); + + std::unique_ptr< QgsProjectProperties > pp = std::make_unique< QgsProjectProperties >( mQgisApp->mapCanvas() ); + QCOMPARE( static_cast( pp->mColorModel->currentData().toInt() ), Qgis::ColorModel::Rgb ); + QVERIFY( !pp->mColorSpace.isValid() ); + QCOMPARE( pp->mColorSpaceName->text(), QStringLiteral( "None" ) ); + QVERIFY( !pp->mRemoveIccProfile->isEnabled() ); + + pp->mColorModel->setCurrentIndex( pp->mColorModel->findData( QVariant::fromValue( Qgis::ColorModel::Cmyk ) ) ); + QCOMPARE( static_cast( pp->mColorModel->currentData().toInt() ), Qgis::ColorModel::Cmyk ); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + + const QString iccProfileFilePath = QStringLiteral( TEST_DATA_DIR ) + "/sRGB2014.icc"; + pp->addIccProfile( iccProfileFilePath ); + QCOMPARE( pp->mColorSpaceName->text(), QStringLiteral( "sRGB2014" ) ); + QVERIFY( pp->mRemoveIccProfile->isEnabled() ); + + pp->apply(); + QCOMPARE( QgsProject::instance()->styleSettings()->colorModel(), Qgis::ColorModel::Cmyk ); + QVERIFY( QgsProject::instance()->styleSettings()->colorSpace().isValid() ); + QCOMPARE( QgsProject::instance()->styleSettings()->colorSpace().description(), QStringLiteral( "sRGB2014" ) ); + + pp->removeIccProfile(); + QVERIFY( !pp->mColorSpace.isValid() ); + QCOMPARE( pp->mColorSpaceName->text(), QStringLiteral( "None" ) ); + QVERIFY( !pp->mRemoveIccProfile->isEnabled() ); + +#endif + +} + + + QGSTEST_MAIN( TestQgsProjectProperties ) #include "testqgsprojectproperties.moc" From b04361cb42c0c610593efdfe15da5e63e158ecfc Mon Sep 17 00:00:00 2001 From: Julien Cabieces Date: Wed, 10 Jul 2024 16:25:35 +0200 Subject: [PATCH 2/8] feat(CMYK): force color model when loading an ICC profile --- src/app/qgsprojectproperties.cpp | 9 +++++++++ tests/src/app/testqgsprojectproperties.cpp | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/app/qgsprojectproperties.cpp b/src/app/qgsprojectproperties.cpp index 2b975a4c2018..575dfddc9693 100644 --- a/src/app/qgsprojectproperties.cpp +++ b/src/app/qgsprojectproperties.cpp @@ -2740,6 +2740,15 @@ void QgsProjectProperties::updateColorSpaceWidget() { mColorSpaceName->setText( mColorSpace.isValid() ? mColorSpace.description() : tr( "None" ) ); mRemoveIccProfile->setEnabled( mColorSpace.isValid() ); + + // force color model index according to color space one + if ( mColorSpace.isValid() ) + { + const Qgis::ColorModel colorModel = QgsColorUtils::toColorModel( mColorSpace.colorModel() ); + mColorModel->setCurrentIndex( mColorModel->findData( QVariant::fromValue( colorModel ) ) ); + } + + mColorModel->setEnabled( !mColorSpace.isValid() ); } #endif diff --git a/tests/src/app/testqgsprojectproperties.cpp b/tests/src/app/testqgsprojectproperties.cpp index 4115f41e3849..d7a79f8ddd66 100644 --- a/tests/src/app/testqgsprojectproperties.cpp +++ b/tests/src/app/testqgsprojectproperties.cpp @@ -278,9 +278,11 @@ void TestQgsProjectProperties::testColorSettings() pp->addIccProfile( iccProfileFilePath ); QCOMPARE( pp->mColorSpaceName->text(), QStringLiteral( "sRGB2014" ) ); QVERIFY( pp->mRemoveIccProfile->isEnabled() ); + QVERIFY( !pp->mColorModel->isEnabled() ); + QCOMPARE( static_cast( pp->mColorModel->currentData().toInt() ), Qgis::ColorModel::Rgb ); pp->apply(); - QCOMPARE( QgsProject::instance()->styleSettings()->colorModel(), Qgis::ColorModel::Cmyk ); + QCOMPARE( QgsProject::instance()->styleSettings()->colorModel(), Qgis::ColorModel::Rgb ); QVERIFY( QgsProject::instance()->styleSettings()->colorSpace().isValid() ); QCOMPARE( QgsProject::instance()->styleSettings()->colorSpace().description(), QStringLiteral( "sRGB2014" ) ); From ea8d12621f25365dcf0871f26593501177bf7462 Mon Sep 17 00:00:00 2001 From: Julien Cabieces Date: Wed, 10 Jul 2024 16:26:08 +0200 Subject: [PATCH 3/8] feat(CMYK): Add tooltips --- src/ui/qgsprojectpropertiesbase.ui | 80 +++++++++++++++++------------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/src/ui/qgsprojectpropertiesbase.ui b/src/ui/qgsprojectpropertiesbase.ui index 75983d60106d..d8089119b07b 100644 --- a/src/ui/qgsprojectpropertiesbase.ui +++ b/src/ui/qgsprojectpropertiesbase.ui @@ -298,7 +298,7 @@ 0 0 - 673 + 522 852 @@ -1413,6 +1413,9 @@ + + <html><head/><body><p>Remove selected ICC profile</p></body></html> + ... @@ -1445,6 +1448,9 @@ + + <html><head/><body><p>Load an ICC profile file and attach it to the project.</p><p>Color model will be updated accordingly.</p></body></html> + @@ -1455,7 +1461,11 @@ - + + + <html><head/><body><p>Color model used as default when selecting a color in the whole application.</p><p>Any color defined in a different color model than the one specified here will be converted to this color model when exporting a layout.</p></body></html> + + @@ -1977,7 +1987,7 @@ 0 0 - 673 + 627 1503 @@ -3487,54 +3497,22 @@
qgscolorbutton.h
1 - - QgsDateTimeEdit - QDateTimeEdit -
qgsdatetimeedit.h
-
- - QgsExtentGroupBox - QgsCollapsibleGroupBox -
qgsextentgroupbox.h
- 1 -
QgsFilterLineEdit QLineEdit
qgsfilterlineedit.h
- - QgsFontButton - QToolButton -
qgsfontbutton.h
-
- - QgsOpacityWidget - QWidget -
qgsopacitywidget.h
- 1 -
QgsProjectionSelectionWidget QWidget
qgsprojectionselectionwidget.h
1
- - QgsPropertyOverrideButton - QToolButton -
qgspropertyoverridebutton.h
-
QgsSpinBox QSpinBox
qgsspinbox.h
- - QgsSymbolButton - QToolButton -
qgssymbolbutton.h
-
QgsScrollArea QScrollArea @@ -3559,6 +3537,38 @@
qgsdatumtransformtablewidget.h
1
+ + QgsDateTimeEdit + QDateTimeEdit +
qgsdatetimeedit.h
+
+ + QgsExtentGroupBox + QgsCollapsibleGroupBox +
qgsextentgroupbox.h
+ 1 +
+ + QgsFontButton + QToolButton +
qgsfontbutton.h
+
+ + QgsOpacityWidget + QWidget +
qgsopacitywidget.h
+ 1 +
+ + QgsPropertyOverrideButton + QToolButton +
qgspropertyoverridebutton.h
+
+ + QgsSymbolButton + QToolButton +
qgssymbolbutton.h
+
QgsProjectionSelectionTreeWidget QWidget From e95ba6e9505c83c04ea050da9e7d299c41b310cb Mon Sep 17 00:00:00 2001 From: Julien Cabieces Date: Tue, 16 Jul 2024 10:15:09 +0200 Subject: [PATCH 4/8] fix(CMYK): treat review comment --- src/app/qgsprojectproperties.cpp | 10 +++++----- src/app/qgsprojectproperties.h | 6 +----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/app/qgsprojectproperties.cpp b/src/app/qgsprojectproperties.cpp index 575dfddc9693..dca7c6656595 100644 --- a/src/app/qgsprojectproperties.cpp +++ b/src/app/qgsprojectproperties.cpp @@ -1036,7 +1036,7 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) mColorSpace = QgsProject::instance()->styleSettings()->colorSpace(); - updateColorSpaceWidget(); + updateColorSpaceWidgets(); #else mIccProfileLabel->setVisible( false ); mColorSpaceName->setVisible( false ); @@ -2708,7 +2708,7 @@ void QgsProjectProperties::addIccProfile() this, tr( "Load ICC Profile" ), QDir::homePath(), - tr( "Style databases" ) + tr( "ICC Profile" ) + QStringLiteral( " (*.icc)" ) ); + tr( "ICC Profile" ) + QStringLiteral( " (*.icc)" ) ); addIccProfile( iccProfileFilePath ); } @@ -2727,16 +2727,16 @@ void QgsProjectProperties::addIccProfile( const QString &iccProfileFilePath ) } mColorSpace = colorSpace; - updateColorSpaceWidget(); + updateColorSpaceWidgets(); } void QgsProjectProperties::removeIccProfile() { mColorSpace = QColorSpace(); - updateColorSpaceWidget(); + updateColorSpaceWidgets(); } -void QgsProjectProperties::updateColorSpaceWidget() +void QgsProjectProperties::updateColorSpaceWidgets() { mColorSpaceName->setText( mColorSpace.isValid() ? mColorSpace.description() : tr( "None" ) ); mRemoveIccProfile->setEnabled( mColorSpace.isValid() ); diff --git a/src/app/qgsprojectproperties.h b/src/app/qgsprojectproperties.h index 5bd992828c6b..026a6f385214 100644 --- a/src/app/qgsprojectproperties.h +++ b/src/app/qgsprojectproperties.h @@ -209,27 +209,23 @@ class APP_EXPORT QgsProjectProperties : public QgsOptionsDialogBase, private Ui: /** * Called whenever user select the add ICC profile button - * \since QGIS 3.40 */ void addIccProfile(); /** * load \a iccProfileFilePath and set resulting color space to project - * \since QGIS 3.40 */ void addIccProfile( const QString &iccProfileFilePath ); /** * Called whenever user select the remove ICC profile button - * \since QGIS 3.40 */ void removeIccProfile(); /** * Update color space widget according to current project color space - * \since QGIS 3.40 */ - void updateColorSpaceWidget(); + void updateColorSpaceWidgets(); #endif From 182ac36afce3522c3895a6bc903ec9862b0be1a9 Mon Sep 17 00:00:00 2001 From: Julien Cabieces Date: Tue, 16 Jul 2024 10:15:51 +0200 Subject: [PATCH 5/8] style(ColorsProperties): Move color properties in its own tab --- src/app/qgisapp.cpp | 1 + src/ui/qgsprojectpropertiesbase.ui | 447 +++++++++++++++-------------- 2 files changed, 227 insertions(+), 221 deletions(-) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 34afe36d8854..db37d483f98d 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -12595,6 +12595,7 @@ QMap< QString, QString > QgisApp::projectPropertiesPagesMap() sProjectPropertiesPagesMap.insert( QCoreApplication::translate( "QgsProjectPropertiesBase", "CRS" ), QStringLiteral( "mProjOptsCRS" ) ); sProjectPropertiesPagesMap.insert( QCoreApplication::translate( "QgsProjectPropertiesBase", "Transformations" ), QStringLiteral( "mProjTransformations" ) ); sProjectPropertiesPagesMap.insert( QCoreApplication::translate( "QgsProjectPropertiesBase", "Styles" ), QStringLiteral( "mProjOptsSymbols" ) ); + sProjectPropertiesPagesMap.insert( QCoreApplication::translate( "QgsProjectPropertiesBase", "Colors" ), QStringLiteral( "mTabColors" ) ); sProjectPropertiesPagesMap.insert( QCoreApplication::translate( "QgsProjectPropertiesBase", "Data Sources" ), QStringLiteral( "mTab_DataSources" ) ); sProjectPropertiesPagesMap.insert( QCoreApplication::translate( "QgsProjectPropertiesBase", "Relations" ), QStringLiteral( "mTabRelations" ) ); sProjectPropertiesPagesMap.insert( QCoreApplication::translate( "QgsProjectPropertiesBase", "Variables" ), QStringLiteral( "mTab_Variables" ) ); diff --git a/src/ui/qgsprojectpropertiesbase.ui b/src/ui/qgsprojectpropertiesbase.ui index d8089119b07b..41cf296f2277 100644 --- a/src/ui/qgsprojectpropertiesbase.ui +++ b/src/ui/qgsprojectpropertiesbase.ui @@ -161,6 +161,15 @@ :/images/themes/default/propertyicons/symbology.svg:/images/themes/default/propertyicons/symbology.svg
+ + + Colors + + + + :/images/themes/default/propertyicons/colors.svg:/images/themes/default/propertyicons/colors.svg + + Data Sources @@ -269,7 +278,7 @@ - 5 + 6 @@ -298,7 +307,7 @@ 0 0 - 522 + 673 852
@@ -1042,8 +1051,8 @@ 0 0 - 279 - 52 + 694 + 789
@@ -1107,8 +1116,8 @@ 0 0 - 523 - 80 + 694 + 789 @@ -1179,11 +1188,11 @@ 0 0 - 673 - 846 + 694 + 789 - + 0 @@ -1388,207 +1397,6 @@ - - - - Options - - - projstyles - - - - - - Assign random colors to symbols - - - - - - - Color model - - - - - - - <html><head/><body><p>Remove selected ICC profile</p></body></html> - - - ... - - - - :/images/themes/default/mActionDeleteSelected.svg:/images/themes/default/mActionDeleteSelected.svg - - - - - - - - - - - - - - Opacity - - - - - - - ICC Profile - - - - - - - <html><head/><body><p>Load an ICC profile file and attach it to the project.</p><p>Color model will be updated accordingly.</p></body></html> - - - - - - - :/images/themes/default/mActionFileOpen.svg:/images/themes/default/mActionFileOpen.svg - - - - - - - <html><head/><body><p>Color model used as default when selecting a color in the whole application.</p><p>Any color defined in a different color model than the one specified here will be converted to this color model when exporting a layout.</p></body></html> - - - - - - - Qt::StrongFocus - - - - - - - - - - Project Colors - - - - - - Copy colors - - - - - - - :/images/themes/default/mActionEditCopy.svg:/images/themes/default/mActionEditCopy.svg - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Add color - - - - - - - :/images/themes/default/symbologyAdd.svg:/images/themes/default/symbologyAdd.svg - - - - - - - Paste colors - - - - - - - :/images/themes/default/mActionEditPaste.svg:/images/themes/default/mActionEditPaste.svg - - - - - - - Remove color - - - - - - - :/images/themes/default/symbologyRemove.svg:/images/themes/default/symbologyRemove.svg - - - - - - - - - - Import colors - - - - - - - - - - :/images/themes/default/mActionFileOpen.svg:/images/themes/default/mActionFileOpen.svg - - - - - - - Export colors - - - - - - - :/images/themes/default/mActionFileSave.svg:/images/themes/default/mActionFileSave.svg - - - - - - @@ -1681,6 +1489,211 @@ + + + + + + Options + + + projstyles + + + + + + Assign random colors to symbols + + + + + + + Color model + + + + + + + <html><head/><body><p>Remove selected ICC profile</p></body></html> + + + ... + + + + :/images/themes/default/mActionDeleteSelected.svg:/images/themes/default/mActionDeleteSelected.svg + + + + + + + + + + + + + + Opacity + + + + + + + ICC Profile + + + + + + + <html><head/><body><p>Load an ICC profile file and attach it to the project.</p><p>Color model will be updated accordingly.</p></body></html> + + + + + + + :/images/themes/default/mActionFileOpen.svg:/images/themes/default/mActionFileOpen.svg + + + + + + + <html><head/><body><p>Color model used as default when selecting a color in the whole application.</p><p>Any color defined in a different color model than the one specified here will be converted to this color model when exporting a layout.</p></body></html> + + + + + + + Qt::StrongFocus + + + + + + + + + + Project Colors + + + + + + Copy colors + + + + + + + :/images/themes/default/mActionEditCopy.svg:/images/themes/default/mActionEditCopy.svg + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Add color + + + + + + + :/images/themes/default/symbologyAdd.svg:/images/themes/default/symbologyAdd.svg + + + + + + + Paste colors + + + + + + + :/images/themes/default/mActionEditPaste.svg:/images/themes/default/mActionEditPaste.svg + + + + + + + Remove color + + + + + + + :/images/themes/default/symbologyRemove.svg:/images/themes/default/symbologyRemove.svg + + + + + + + + + + Import colors + + + + + + + + + + :/images/themes/default/mActionFileOpen.svg:/images/themes/default/mActionFileOpen.svg + + + + + + + Export colors + + + + + + + :/images/themes/default/mActionFileSave.svg:/images/themes/default/mActionFileSave.svg + + + + + + + + @@ -1904,8 +1917,8 @@ 0 0 - 151 - 42 + 694 + 789 @@ -1987,7 +2000,7 @@ 0 0 - 627 + 673 1503 @@ -3643,14 +3656,6 @@ mStyleFillSymbol mStyleColorRampSymbol mStyleTextFormat - mDefaultOpacityWidget - cbxStyleRandomColors - mButtonAddColor - mButtonRemoveColor - mButtonCopyColors - mButtonPasteColors - mButtonImportColors - mButtonExportColors mListStyleDatabases mButtonAddStyleDatabase mButtonRemoveStyleDatabase From 38e25d3be0e9b6036882d11ad36a18402d823b93 Mon Sep 17 00:00:00 2001 From: Julien Cabieces Date: Tue, 16 Jul 2024 10:26:11 +0200 Subject: [PATCH 6/8] fix(ColorProperties): fix test on Qt < 6.8.0 --- tests/src/app/testqgsprojectproperties.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/src/app/testqgsprojectproperties.cpp b/tests/src/app/testqgsprojectproperties.cpp index d7a79f8ddd66..4141fab6529e 100644 --- a/tests/src/app/testqgsprojectproperties.cpp +++ b/tests/src/app/testqgsprojectproperties.cpp @@ -266,8 +266,15 @@ void TestQgsProjectProperties::testColorSettings() std::unique_ptr< QgsProjectProperties > pp = std::make_unique< QgsProjectProperties >( mQgisApp->mapCanvas() ); QCOMPARE( static_cast( pp->mColorModel->currentData().toInt() ), Qgis::ColorModel::Rgb ); QVERIFY( !pp->mColorSpace.isValid() ); +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) QCOMPARE( pp->mColorSpaceName->text(), QStringLiteral( "None" ) ); QVERIFY( !pp->mRemoveIccProfile->isEnabled() ); +#else + QVERIFY( !pp->mRemoveIccProfile->isVisible() ); + QVERIFY( !pp->mAddIccProfile->isVisible() ); + QVERIFY( !pp->mColorSpaceName->isVisible() ); + QVERIFY( !pp->mIccProfileLabel->isVisible() ); +#endif pp->mColorModel->setCurrentIndex( pp->mColorModel->findData( QVariant::fromValue( Qgis::ColorModel::Cmyk ) ) ); QCOMPARE( static_cast( pp->mColorModel->currentData().toInt() ), Qgis::ColorModel::Cmyk ); From be954283dc4f0cff261e61d92d1144d62308754d Mon Sep 17 00:00:00 2001 From: Julien Cabieces Date: Tue, 16 Jul 2024 14:03:13 +0200 Subject: [PATCH 7/8] feat(ICCProfile): Add an action to save an ICC profile file --- .../core/auto_generated/qgscolorutils.sip.in | 10 +++ .../core/auto_generated/qgscolorutils.sip.in | 10 +++ src/app/qgsprojectproperties.cpp | 19 +++++ src/app/qgsprojectproperties.h | 5 ++ src/core/qgscolorutils.cpp | 16 +++++ src/core/qgscolorutils.h | 9 +++ src/ui/qgsprojectpropertiesbase.ui | 72 +++++++++++-------- tests/src/app/testqgsprojectproperties.cpp | 1 + tests/src/python/test_qgscolorutils.py | 22 ++++++ 9 files changed, 135 insertions(+), 29 deletions(-) diff --git a/python/PyQt6/core/auto_generated/qgscolorutils.sip.in b/python/PyQt6/core/auto_generated/qgscolorutils.sip.in index 81063d529b11..728550ba916e 100644 --- a/python/PyQt6/core/auto_generated/qgscolorutils.sip.in +++ b/python/PyQt6/core/auto_generated/qgscolorutils.sip.in @@ -95,6 +95,16 @@ message .. versionadded:: 3.40 %End + static QString saveIccProfile( const QColorSpace &colorSpace, const QString &iccProfileFilePath ); +%Docstring +Save color space ``colorSpace`` to an ICC profile file ``iccProfileFilePath``. + +:return: error message if an error occurred else empty string. + +.. versionadded:: 3.40 +%End + + diff --git a/python/core/auto_generated/qgscolorutils.sip.in b/python/core/auto_generated/qgscolorutils.sip.in index 81063d529b11..728550ba916e 100644 --- a/python/core/auto_generated/qgscolorutils.sip.in +++ b/python/core/auto_generated/qgscolorutils.sip.in @@ -95,6 +95,16 @@ message .. versionadded:: 3.40 %End + static QString saveIccProfile( const QColorSpace &colorSpace, const QString &iccProfileFilePath ); +%Docstring +Save color space ``colorSpace`` to an ICC profile file ``iccProfileFilePath``. + +:return: error message if an error occurred else empty string. + +.. versionadded:: 3.40 +%End + + diff --git a/src/app/qgsprojectproperties.cpp b/src/app/qgsprojectproperties.cpp index dca7c6656595..fbab96ec6504 100644 --- a/src/app/qgsprojectproperties.cpp +++ b/src/app/qgsprojectproperties.cpp @@ -138,6 +138,7 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) connect( mAddIccProfile, &QToolButton::clicked, this, static_cast( &QgsProjectProperties::addIccProfile ) ); connect( mRemoveIccProfile, &QToolButton::clicked, this, &QgsProjectProperties::removeIccProfile ); + connect( mSaveIccProfile, &QToolButton::clicked, this, &QgsProjectProperties::saveIccProfile ); #endif // QgsOptionsDialogBase handles saving/restoring of geometry, splitter and current tab states, @@ -1042,6 +1043,7 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa mColorSpaceName->setVisible( false ); mAddIccProfile->setVisible( false ); mRemoveIccProfile->setVisible( false ); + mSaveIccProfile->setVisible( false ); #endif // Default alpha transparency mDefaultOpacityWidget->setOpacity( QgsProject::instance()->styleSettings()->defaultSymbolOpacity() ); @@ -2736,10 +2738,27 @@ void QgsProjectProperties::removeIccProfile() updateColorSpaceWidgets(); } +void QgsProjectProperties::saveIccProfile() +{ + QString fileName = QFileDialog::getSaveFileName( this, tr( "Save ICC profile" ), QDir::homePath(), + tr( "ICC profile files (*.icc *.ICC)" ) ); + + if ( fileName.isEmpty() ) + return; + + const QString error = QgsColorUtils::saveIccProfile( mColorSpace, fileName ); + if ( !error.isEmpty() ) + { + QMessageBox::warning( this, tr( "Save ICC profile" ), error ); + } +} + + void QgsProjectProperties::updateColorSpaceWidgets() { mColorSpaceName->setText( mColorSpace.isValid() ? mColorSpace.description() : tr( "None" ) ); mRemoveIccProfile->setEnabled( mColorSpace.isValid() ); + mSaveIccProfile->setEnabled( mColorSpace.isValid() ); // force color model index according to color space one if ( mColorSpace.isValid() ) diff --git a/src/app/qgsprojectproperties.h b/src/app/qgsprojectproperties.h index 026a6f385214..d0ba5799a4f7 100644 --- a/src/app/qgsprojectproperties.h +++ b/src/app/qgsprojectproperties.h @@ -222,6 +222,11 @@ class APP_EXPORT QgsProjectProperties : public QgsOptionsDialogBase, private Ui: */ void removeIccProfile(); + /** + * Called whenever user select the save ICC profile button + */ + void saveIccProfile(); + /** * Update color space widget according to current project color space */ diff --git a/src/core/qgscolorutils.cpp b/src/core/qgscolorutils.cpp index 514d6173b4e9..4c37a41d2412 100644 --- a/src/core/qgscolorutils.cpp +++ b/src/core/qgscolorutils.cpp @@ -377,6 +377,22 @@ QColorSpace QgsColorUtils::iccProfile( const QString &iccProfileFilePath, QStrin return colorSpace; } + +QString QgsColorUtils::saveIccProfile( const QColorSpace &colorSpace, const QString &iccProfileFilePath ) +{ + if ( !colorSpace.isValid() ) + return QObject::tr( "Invalid ICC profile" ); + + QFile iccProfile( iccProfileFilePath ); + if ( !iccProfile.open( QIODevice::WriteOnly ) ) + return QObject::tr( "File access error '%1'" ).arg( iccProfileFilePath ); + + if ( iccProfile.write( colorSpace.iccProfile() ) < 0 ) + return QObject::tr( "Error while writing to file '%1'" ).arg( iccProfileFilePath ); + + return QString(); +} + #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) Qgis::ColorModel QgsColorUtils::toColorModel( QColorSpace::ColorModel colorModel, bool *ok ) diff --git a/src/core/qgscolorutils.h b/src/core/qgscolorutils.h index 593fc3133243..a31b8081eac8 100644 --- a/src/core/qgscolorutils.h +++ b/src/core/qgscolorutils.h @@ -108,6 +108,15 @@ class CORE_EXPORT QgsColorUtils */ static QColorSpace iccProfile( const QString &iccProfileFilePath, QString &errorMsg SIP_OUT ); + /** + * Save color space \a colorSpace to an ICC profile file \a iccProfileFilePath. + * \returns error message if an error occurred else empty string. + * + * \since QGIS 3.40 + */ + static QString saveIccProfile( const QColorSpace &colorSpace, const QString &iccProfileFilePath ); + + #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) /** diff --git a/src/ui/qgsprojectpropertiesbase.ui b/src/ui/qgsprojectpropertiesbase.ui index 41cf296f2277..153253a3dbec 100644 --- a/src/ui/qgsprojectpropertiesbase.ui +++ b/src/ui/qgsprojectpropertiesbase.ui @@ -1500,17 +1500,45 @@ projstyles - - + + - Assign random colors to symbols + ICC Profile - - + + - Color model + Opacity + + + + + + + Qt::StrongFocus + + + + + + + <html><head/><body><p>Load an ICC profile file and attach it to the project.</p><p>Color model will be updated accordingly.</p></body></html> + + + + + + + :/images/themes/default/mActionFileOpen.svg:/images/themes/default/mActionFileOpen.svg + + + + + + + <html><head/><body><p>Color model used as default when selecting a color in the whole application.</p><p>Any color defined in a different color model than the one specified here will be converted to this color model when exporting a layout.</p></body></html> @@ -1535,45 +1563,31 @@ - - + + - Opacity + Color model - - + + - ICC Profile + Assign random colors to symbols - + - <html><head/><body><p>Load an ICC profile file and attach it to the project.</p><p>Color model will be updated accordingly.</p></body></html> + <html><head/><body><p>Save ICC profile</p></body></html> - :/images/themes/default/mActionFileOpen.svg:/images/themes/default/mActionFileOpen.svg - - - - - - - <html><head/><body><p>Color model used as default when selecting a color in the whole application.</p><p>Any color defined in a different color model than the one specified here will be converted to this color model when exporting a layout.</p></body></html> - - - - - - - Qt::StrongFocus + :/images/themes/default/mActionFileSave.svg:/images/themes/default/mActionFileSave.svg diff --git a/tests/src/app/testqgsprojectproperties.cpp b/tests/src/app/testqgsprojectproperties.cpp index 4141fab6529e..624ba5cda1b5 100644 --- a/tests/src/app/testqgsprojectproperties.cpp +++ b/tests/src/app/testqgsprojectproperties.cpp @@ -272,6 +272,7 @@ void TestQgsProjectProperties::testColorSettings() #else QVERIFY( !pp->mRemoveIccProfile->isVisible() ); QVERIFY( !pp->mAddIccProfile->isVisible() ); + QVERIFY( !pp->mSaveIccProfile->isVisible() ); QVERIFY( !pp->mColorSpaceName->isVisible() ); QVERIFY( !pp->mIccProfileLabel->isVisible() ); #endif diff --git a/tests/src/python/test_qgscolorutils.py b/tests/src/python/test_qgscolorutils.py index 59716feeaaf5..ad452132ff24 100644 --- a/tests/src/python/test_qgscolorutils.py +++ b/tests/src/python/test_qgscolorutils.py @@ -9,11 +9,18 @@ __date__ = '06/07/2022' __copyright__ = 'Copyright 2022, The QGIS Project' +import os + +from qgis.PyQt.QtCore import QTemporaryDir from qgis.PyQt.QtGui import QColor from qgis.PyQt.QtXml import QDomDocument from qgis.core import QgsColorUtils, QgsReadWriteContext, QgsSymbolLayerUtils from qgis.testing import unittest +from utilities import unitTestDataPath + +TEST_DATA_DIR = unitTestDataPath() + class TestQgsColorUtils(unittest.TestCase): @@ -288,6 +295,21 @@ def test_color_string_compat(self): self.assertAlmostEqual(res.blue(), 23, delta=1) self.assertEqual(res.alpha(), 220) + def test_icc_profile(self): + """ + Test ICC profile load and save method + """ + + iccProfileFilePath = os.path.join(TEST_DATA_DIR, "sRGB2014.icc") + colorSpace, errorMsg = QgsColorUtils.iccProfile(iccProfileFilePath) + self.assertTrue(colorSpace.isValid()) + + tmpDir = QTemporaryDir() + tmpFile = f"{tmpDir.path()}/test.icc" + + error = QgsColorUtils.saveIccProfile(colorSpace, tmpFile) + self.assertTrue(not error) + if __name__ == '__main__': unittest.main() From 48de201df0d9eba9a0c3a1313126b721ab4719c8 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 17 Jul 2024 13:29:15 +1000 Subject: [PATCH 8/8] Apply suggestions from code review --- src/app/qgsprojectproperties.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/qgsprojectproperties.cpp b/src/app/qgsprojectproperties.cpp index fbab96ec6504..93f7e2744baa 100644 --- a/src/app/qgsprojectproperties.cpp +++ b/src/app/qgsprojectproperties.cpp @@ -2740,12 +2740,13 @@ void QgsProjectProperties::removeIccProfile() void QgsProjectProperties::saveIccProfile() { - QString fileName = QFileDialog::getSaveFileName( this, tr( "Save ICC profile" ), QDir::homePath(), + QString fileName = QFileDialog::getSaveFileName( this, tr( "Save ICC Profile" ), QDir::homePath(), tr( "ICC profile files (*.icc *.ICC)" ) ); if ( fileName.isEmpty() ) return; + fileName = QgsFileUtils::ensureFileNameHasExtension( fileName, { QStringLiteral( "icc" ) } ); const QString error = QgsColorUtils::saveIccProfile( mColorSpace, fileName ); if ( !error.isEmpty() ) {