feat(ICCProfile): Add an action to save an ICC profile file

This commit is contained in:
Julien Cabieces 2024-07-16 14:03:13 +02:00 committed by Nyall Dawson
parent b1181118a7
commit 76a4202f7e
9 changed files with 135 additions and 29 deletions

View File

@ -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

View File

@ -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

View File

@ -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,
@ -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( "<i>None</i>" ) );
mRemoveIccProfile->setEnabled( mColorSpace.isValid() );
mSaveIccProfile->setEnabled( mColorSpace.isValid() );
// force color model index according to color space one
if ( mColorSpace.isValid() )

View File

@ -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
*/

View File

@ -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 )

View File

@ -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)
/**

View File

@ -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>
@ -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>

View File

@ -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

View File

@ -9,11 +9,18 @@ __author__ = 'Nyall Dawson'
__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 @@ class TestQgsColorUtils(unittest.TestCase):
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()