Skip to content

Commit

Permalink
feat(ICCProfile): Add an action to save an ICC profile file
Browse files Browse the repository at this point in the history
  • Loading branch information
troopa81 authored and nyalldawson committed Jul 17, 2024
1 parent b118111 commit 76a4202
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 29 deletions.
10 changes: 10 additions & 0 deletions python/PyQt6/core/auto_generated/qgscolorutils.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -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





Expand Down
10 changes: 10 additions & 0 deletions python/core/auto_generated/qgscolorutils.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -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





Expand Down
19 changes: 19 additions & 0 deletions src/app/qgsprojectproperties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<void ( QgsProjectProperties::* )()>( &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,
Expand Down Expand Up @@ -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() );
Expand Down Expand Up @@ -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( "<i>None</i>" ) );
mRemoveIccProfile->setEnabled( mColorSpace.isValid() );
mSaveIccProfile->setEnabled( mColorSpace.isValid() );

// force color model index according to color space one
if ( mColorSpace.isValid() )
Expand Down
5 changes: 5 additions & 0 deletions src/app/qgsprojectproperties.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
16 changes: 16 additions & 0 deletions src/core/qgscolorutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand Down
9 changes: 9 additions & 0 deletions src/core/qgscolorutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)

/**
Expand Down
72 changes: 43 additions & 29 deletions src/ui/qgsprojectpropertiesbase.ui
Original file line number Diff line number Diff line change
Expand Up @@ -1500,17 +1500,45 @@
<string notr="true">projstyles</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="6" column="0" colspan="3">
<widget class="QCheckBox" name="cbxStyleRandomColors">
<item row="1" column="0">
<widget class="QLabel" name="mIccProfileLabel">
<property name="text">
<string>Assign random colors to symbols</string>
<string>ICC Profile</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_14">
<item row="3" column="0">
<widget class="QLabel" name="label_27">
<property name="text">
<string>Color model</string>
<string>Opacity</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="4">
<widget class="QgsOpacityWidget" name="mDefaultOpacityWidget" native="true">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QToolButton" name="mAddIccProfile">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Load an ICC profile file and attach it to the project.&lt;/p&gt;&lt;p&gt;Color model will be updated accordingly.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionFileOpen.svg</normaloff>:/images/themes/default/mActionFileOpen.svg</iconset>
</property>
</widget>
</item>
<item row="0" column="1" colspan="4">
<widget class="QComboBox" name="mColorModel">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Color model used as default when selecting a color in the whole application.&lt;/p&gt;&lt;p&gt;Any color defined in a different color model than the one specified here will be converted to this color model when exporting a layout.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
Expand All @@ -1535,45 +1563,31 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_27">
<item row="0" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Opacity</string>
<string>Color model</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="mIccProfileLabel">
<item row="6" column="0" colspan="3">
<widget class="QCheckBox" name="cbxStyleRandomColors">
<property name="text">
<string>ICC Profile</string>
<string>Assign random colors to symbols</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QToolButton" name="mAddIccProfile">
<widget class="QToolButton" name="mSaveIccProfile">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Load an ICC profile file and attach it to the project.&lt;/p&gt;&lt;p&gt;Color model will be updated accordingly.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Save ICC profile&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionFileOpen.svg</normaloff>:/images/themes/default/mActionFileOpen.svg</iconset>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QComboBox" name="mColorModel">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Color model used as default when selecting a color in the whole application.&lt;/p&gt;&lt;p&gt;Any color defined in a different color model than the one specified here will be converted to this color model when exporting a layout.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="3">
<widget class="QgsOpacityWidget" name="mDefaultOpacityWidget" native="true">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
<normaloff>:/images/themes/default/mActionFileSave.svg</normaloff>:/images/themes/default/mActionFileSave.svg</iconset>
</property>
</widget>
</item>
Expand Down
1 change: 1 addition & 0 deletions tests/src/app/testqgsprojectproperties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions tests/src/python/test_qgscolorutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):

Expand Down Expand Up @@ -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()

0 comments on commit 76a4202

Please sign in to comment.