[FEATURE][composer] Data definable item frame and background colors

This commit is contained in:
Nyall Dawson 2017-01-17 12:21:19 +10:00
parent ad2324021d
commit d6c7569dda
9 changed files with 206 additions and 31 deletions

View File

@ -32,6 +32,8 @@ class QgsComposerObject : QObject, QgsExpressionContextGenerator
Transparency, /*!< item transparency */
BlendMode, /*!< item blend mode */
ExcludeFromExports, /*!< exclude item from exports */
FrameColor, //!< Item frame color
BackgroundColor, //!< Item background color
//composer map
MapRotation, /*!< map rotation */
MapScale, /*!< map scale */

View File

@ -541,6 +541,10 @@ void QgsComposerItemWidget::populateDataDefinedButtons()
QgsDataDefinedButtonV2::String, QgsDataDefinedButtonV2::blendModesDesc() );
mConfigObject->registerDataDefinedButton( mExcludePrintsDDBtn, QgsComposerObject::ExcludeFromExports,
QgsDataDefinedButtonV2::String, QgsDataDefinedButtonV2::boolDesc() );
mConfigObject->registerDataDefinedButton( mItemFrameColorDDBtn, QgsComposerObject::FrameColor,
QgsDataDefinedButtonV2::String, QgsDataDefinedButtonV2::colorAlphaDesc() );
mConfigObject->registerDataDefinedButton( mItemBackgroundColorDDBtn, QgsComposerObject::BackgroundColor,
QgsDataDefinedButtonV2::String, QgsDataDefinedButtonV2::colorAlphaDesc() );
}
void QgsComposerItemWidget::setValuesForGuiElements()

View File

@ -54,7 +54,6 @@ QgsComposerItem::QgsComposerItem( QgsComposition* composition, bool manageZValue
, mFrame( false )
, mBackground( true )
, mBackgroundColor( QColor( 255, 255, 255, 255 ) )
, mFrameJoinStyle( Qt::MiterJoin )
, mItemPositionLocked( false )
, mLastValidViewScaleFactor( -1 )
, mItemRotation( 0 )
@ -81,9 +80,9 @@ QgsComposerItem::QgsComposerItem( qreal x, qreal y, qreal width, qreal height, Q
, mHAlignSnapItem( nullptr )
, mVAlignSnapItem( nullptr )
, mFrame( false )
, mFrameColor( QColor( 0, 0, 0 ) )
, mBackground( true )
, mBackgroundColor( QColor( 255, 255, 255, 255 ) )
, mFrameJoinStyle( Qt::MiterJoin )
, mItemPositionLocked( false )
, mLastValidViewScaleFactor( -1 )
, mItemRotation( 0 )
@ -107,9 +106,9 @@ void QgsComposerItem::init( const bool manageZValue )
{
setFlag( QGraphicsItem::ItemIsSelectable, true );
//set default pen and brush
setBrush( QBrush( QColor( 255, 255, 255, 255 ) ) );
QPen defaultPen( QColor( 0, 0, 0 ) );
defaultPen.setWidthF( 0.3 );
setBrush( mBackgroundColor );
QPen defaultPen( mFrameColor );
defaultPen.setWidthF( mFrameWidth );
defaultPen.setJoinStyle( mFrameJoinStyle );
setPen( defaultPen );
//let z-Value be managed by composition
@ -192,7 +191,7 @@ bool QgsComposerItem::_writeXml( QDomElement& itemElem, QDomDocument& doc ) cons
composerItemElem.setAttribute( QStringLiteral( "height" ), QString::number( rect().height() ) );
composerItemElem.setAttribute( QStringLiteral( "positionMode" ), QString::number( static_cast< int >( mLastUsedPositionMode ) ) );
composerItemElem.setAttribute( QStringLiteral( "zValue" ), QString::number( zValue() ) );
composerItemElem.setAttribute( QStringLiteral( "outlineWidth" ), QString::number( pen().widthF() ) );
composerItemElem.setAttribute( QStringLiteral( "outlineWidth" ), QString::number( mFrameWidth ) );
composerItemElem.setAttribute( QStringLiteral( "frameJoinStyle" ), QgsSymbolLayerUtils::encodePenJoinStyle( mFrameJoinStyle ) );
composerItemElem.setAttribute( QStringLiteral( "itemRotation" ), QString::number( mItemRotation ) );
composerItemElem.setAttribute( QStringLiteral( "uuid" ), mUuid );
@ -212,20 +211,18 @@ bool QgsComposerItem::_writeXml( QDomElement& itemElem, QDomDocument& doc ) cons
//frame color
QDomElement frameColorElem = doc.createElement( QStringLiteral( "FrameColor" ) );
QColor frameColor = pen().color();
frameColorElem.setAttribute( QStringLiteral( "red" ), QString::number( frameColor.red() ) );
frameColorElem.setAttribute( QStringLiteral( "green" ), QString::number( frameColor.green() ) );
frameColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( frameColor.blue() ) );
frameColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( frameColor.alpha() ) );
frameColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mFrameColor.red() ) );
frameColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mFrameColor.green() ) );
frameColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mFrameColor.blue() ) );
frameColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mFrameColor.alpha() ) );
composerItemElem.appendChild( frameColorElem );
//background color
QDomElement bgColorElem = doc.createElement( QStringLiteral( "BackgroundColor" ) );
QColor bgColor = brush().color();
bgColorElem.setAttribute( QStringLiteral( "red" ), QString::number( bgColor.red() ) );
bgColorElem.setAttribute( QStringLiteral( "green" ), QString::number( bgColor.green() ) );
bgColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( bgColor.blue() ) );
bgColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( bgColor.alpha() ) );
bgColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mBackgroundColor.red() ) );
bgColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mBackgroundColor.green() ) );
bgColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mBackgroundColor.blue() ) );
bgColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mBackgroundColor.alpha() ) );
composerItemElem.appendChild( bgColorElem );
//blend mode
@ -335,6 +332,8 @@ bool QgsComposerItem::_readXml( const QDomElement& itemElem, const QDomDocument&
setZValue( itemElem.attribute( QStringLiteral( "zValue" ) ).toDouble() );
QgsExpressionContext context = createExpressionContext();
//pen
QDomNodeList frameColorList = itemElem.elementsByTagName( QStringLiteral( "FrameColor" ) );
if ( !frameColorList.isEmpty() )
@ -353,10 +352,14 @@ bool QgsComposerItem::_readXml( const QDomElement& itemElem, const QDomDocument&
if ( redOk && greenOk && blueOk && alphaOk && widthOk )
{
QPen framePen( QColor( penRed, penGreen, penBlue, penAlpha ) );
framePen.setWidthF( penWidth );
mFrameColor = QColor( penRed, penGreen, penBlue, penAlpha );
mFrameWidth = penWidth;
QPen framePen( mFrameColor );
framePen.setWidthF( mFrameWidth );
framePen.setJoinStyle( mFrameJoinStyle );
setPen( framePen );
//apply any data defined settings
refreshFrameColor( false, context );
}
}
@ -373,9 +376,11 @@ bool QgsComposerItem::_readXml( const QDomElement& itemElem, const QDomDocument&
bgAlpha = bgColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
if ( redOk && greenOk && blueOk && alphaOk )
{
QColor brushColor( bgRed, bgGreen, bgBlue, bgAlpha );
setBackgroundColor( brushColor );
mBackgroundColor = QColor( bgRed, bgGreen, bgBlue, bgAlpha );
setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
}
//apply any data defined settings
refreshBackgroundColor( false, context );
}
//blend mode
@ -407,26 +412,31 @@ void QgsComposerItem::setFrameEnabled( const bool drawFrame )
void QgsComposerItem::setFrameOutlineColor( const QColor &color )
{
QPen itemPen = pen();
if ( itemPen.color() == color )
if ( mFrameColor == color )
{
//no change
return;
}
itemPen.setColor( color );
mFrameColor = color;
QPen itemPen = pen();
itemPen.setColor( mFrameColor );
setPen( itemPen );
// apply any datadefined overrides
QgsExpressionContext context = createExpressionContext();
refreshFrameColor( true, context );
emit frameChanged();
}
void QgsComposerItem::setFrameOutlineWidth( const double outlineWidth )
{
QPen itemPen = pen();
if ( qgsDoubleNear( itemPen.widthF(), outlineWidth ) )
if ( qgsDoubleNear( mFrameWidth, outlineWidth ) )
{
//no change
return;
}
itemPen.setWidthF( outlineWidth );
mFrameWidth = outlineWidth;
QPen itemPen = pen();
itemPen.setWidthF( mFrameWidth );
setPen( itemPen );
emit frameChanged();
}
@ -844,6 +854,9 @@ void QgsComposerItem::setBackgroundColor( const QColor& backgroundColor )
{
mBackgroundColor = backgroundColor;
setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
// apply any datadefined overrides
QgsExpressionContext context = createExpressionContext();
refreshBackgroundColor( true, context );
}
void QgsComposerItem::setBlendMode( const QPainter::CompositionMode blendMode )
@ -897,6 +910,48 @@ void QgsComposerItem::refreshTransparency( const bool updateItem, const QgsExpre
}
}
void QgsComposerItem::refreshFrameColor( const bool updateItem, const QgsExpressionContext& context )
{
//data defined outline color set?
bool ok = false;
QColor frameColor = mProperties.valueAsColor( QgsComposerObject::FrameColor, context, mFrameColor, &ok );
if ( ok )
{
QPen itemPen = pen();
itemPen.setColor( frameColor );
setPen( itemPen );
}
else
{
QPen itemPen = pen();
itemPen.setColor( mFrameColor );
setPen( itemPen );
}
if ( updateItem )
{
update();
}
}
void QgsComposerItem::refreshBackgroundColor( const bool updateItem, const QgsExpressionContext& context )
{
//data defined color set?
bool ok = false;
QColor backgroundColor = mProperties.valueAsColor( QgsComposerObject::BackgroundColor, context, mBackgroundColor, &ok );
if ( ok )
{
setBrush( QBrush( backgroundColor, Qt::SolidPattern ) );
}
else
{
setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
}
if ( updateItem )
{
update();
}
}
void QgsComposerItem::setEffectsEnabled( const bool effectsEnabled )
{
//enable or disable the QgsComposerEffect applied to this item
@ -1087,6 +1142,14 @@ void QgsComposerItem::refreshDataDefinedProperty( const QgsComposerObject::DataD
{
refreshBlendMode( *evalContext );
}
if ( property == QgsComposerObject::FrameColor || property == QgsComposerObject::AllProperties )
{
refreshFrameColor( false, *evalContext );
}
if ( property == QgsComposerObject::BackgroundColor || property == QgsComposerObject::AllProperties )
{
refreshBackgroundColor( false, *evalContext );
}
if ( property == QgsComposerObject::ExcludeFromExports || property == QgsComposerObject::AllProperties )
{
bool exclude = mExcludeFromExports;

View File

@ -260,7 +260,7 @@ class CORE_EXPORT QgsComposerItem: public QgsComposerObject, public QGraphicsRec
* @see frameJoinStyle
* @see setFrameOutlineColor
*/
QColor frameOutlineColor() const { return pen().color(); }
QColor frameOutlineColor() const { return mFrameColor; }
/** Sets frame outline width
* @param outlineWidth new width for outline frame
@ -280,7 +280,7 @@ class CORE_EXPORT QgsComposerItem: public QgsComposerObject, public QGraphicsRec
* @see frameJoinStyle
* @see frameOutlineColor
*/
double frameOutlineWidth() const { return pen().widthF(); }
double frameOutlineWidth() const { return mFrameWidth; }
/** Returns the join style used for drawing the item's frame
* @returns Join style for outline frame
@ -564,12 +564,17 @@ class CORE_EXPORT QgsComposerItem: public QgsComposerObject, public QGraphicsRec
//! True if item fram needs to be painted
bool mFrame;
//! Item frame color
QColor mFrameColor;
//! Item frame width
double mFrameWidth = 0.3;
//! Frame join style
Qt::PenJoinStyle mFrameJoinStyle = Qt::MiterJoin;
//! True if item background needs to be painted
bool mBackground;
//! Background color
QColor mBackgroundColor;
//! Frame join style
Qt::PenJoinStyle mFrameJoinStyle;
/** True if item position and size cannot be changed with mouse move
*/
@ -706,6 +711,20 @@ class CORE_EXPORT QgsComposerItem: public QgsComposerObject, public QGraphicsRec
*/
void refreshTransparency( const bool updateItem = true, const QgsExpressionContext &context = QgsExpressionContext() );
/** Refresh item's frame color, considering data defined transparency
* @param updateItem set to false to prevent the item being automatically updated
* after the frame color is set
* @param context expression context for evaulating data defined transparency
*/
void refreshFrameColor( const bool updateItem = true, const QgsExpressionContext &context = QgsExpressionContext() );
/** Refresh item's transparency, considering data defined transparency
* @param updateItem set to false to prevent the item being automatically updated
* after the background color is set
* @param context expression context for evaulating data defined transparency
*/
void refreshBackgroundColor( const bool updateItem = true, const QgsExpressionContext &context = QgsExpressionContext() );
/** Refresh item's blend mode, considering data defined blend mode
* @note this method was added in version 2.5
*/

View File

@ -40,6 +40,8 @@ const QgsPropertyDefinition QgsComposerObject::sPropertyNameMap
{ QgsComposerObject::Transparency, "dataDefinedTransparency" },
{ QgsComposerObject::BlendMode, "dataDefinedBlendMode" },
{ QgsComposerObject::ExcludeFromExports, "dataDefinedExcludeExports"},
{ QgsComposerObject::FrameColor, "dataDefinedFrameColor"},
{ QgsComposerObject::BackgroundColor, "dataDefinedBackgroundColor"},
{ QgsComposerObject::MapRotation, "dataDefinedMapRotation" },
{ QgsComposerObject::MapScale, "dataDefinedMapScale" },
{ QgsComposerObject::MapXMin, "dataDefinedMapXMin" },

View File

@ -59,6 +59,8 @@ class CORE_EXPORT QgsComposerObject: public QObject, public QgsExpressionContext
Transparency, //!< Item transparency
BlendMode, //!< Item blend mode
ExcludeFromExports, //!< Exclude item from exports
FrameColor, //!< Item frame color
BackgroundColor, //!< Item background color
//composer map
MapRotation, //!< Map rotation
MapScale, //!< Map scale

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>290</width>
<height>847</height>
<height>1017</height>
</rect>
</property>
<property name="windowTitle">
@ -453,6 +453,13 @@
</property>
</spacer>
</item>
<item>
<widget class="QgsDataDefinedButtonV2" name="mItemFrameColorDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
@ -572,6 +579,13 @@
</property>
</spacer>
</item>
<item>
<widget class="QgsDataDefinedButtonV2" name="mItemBackgroundColorDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
@ -816,10 +830,12 @@
<tabstop>mItemRotationDDBtn</tabstop>
<tabstop>mFrameGroupBox</tabstop>
<tabstop>mFrameColorButton</tabstop>
<tabstop>mItemFrameColorDDBtn</tabstop>
<tabstop>mOutlineWidthSpinBox</tabstop>
<tabstop>mFrameJoinStyleCombo</tabstop>
<tabstop>mBackgroundGroupBox</tabstop>
<tabstop>mBackgroundColorButton</tabstop>
<tabstop>mItemBackgroundColorDDBtn</tabstop>
<tabstop>groupBox</tabstop>
<tabstop>mItemIdLineEdit</tabstop>
<tabstop>groupRendering</tabstop>

View File

@ -25,6 +25,7 @@ ADD_PYTHON_TEST(PyQgsColorScheme test_qgscolorscheme.py)
ADD_PYTHON_TEST(PyQgsColorSchemeRegistry test_qgscolorschemeregistry.py)
ADD_PYTHON_TEST(PyQgsComposerEffects test_qgscomposereffects.py)
ADD_PYTHON_TEST(PyQgsComposerHtml test_qgscomposerhtml.py)
ADD_PYTHON_TEST(PyQgsComposerItem test_qgscomposeritem.py)
ADD_PYTHON_TEST(PyQgsComposerLabel test_qgscomposerlabel.py)
ADD_PYTHON_TEST(PyQgsComposerLegend test_qgscomposerlegend.py)
ADD_PYTHON_TEST(PyQgsComposerMap test_qgscomposermap.py)

View File

@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsComposerItem.
.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"""
__author__ = '(C) 2017 by Nyall Dawson'
__date__ = '17/01/2017'
__copyright__ = 'Copyright 2017, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
import qgis # NOQA
from qgis.testing import start_app, unittest
from qgis.core import (QgsProject,
QgsMapSettings,
QgsComposition,
QgsComposerLabel,
QgsComposerObject,
QgsExpressionBasedProperty)
from qgis.PyQt.QtGui import (QColor)
start_app()
class TestQgsComposerItem(unittest.TestCase):
def testDataDefinedFrameColor(self):
mapSettings = QgsMapSettings()
composition = QgsComposition(mapSettings, QgsProject.instance())
composition.setPaperSize(297, 210)
item = QgsComposerLabel(composition)
composition.addComposerLabel(item)
item.setFrameOutlineColor(QColor(255, 0, 0))
self.assertEqual(item.frameOutlineColor(), QColor(255, 0, 0))
self.assertEqual(item.pen().color().name(), QColor(255, 0, 0).name())
item.dataDefinedProperties().setProperty(QgsComposerObject.FrameColor, QgsExpressionBasedProperty("'blue'"))
item.refreshDataDefinedProperty()
self.assertEqual(item.frameOutlineColor(), QColor(255, 0, 0)) # should not change
self.assertEqual(item.pen().color().name(), QColor(0, 0, 255).name())
def testDataDefinedBackgroundColor(self):
mapSettings = QgsMapSettings()
composition = QgsComposition(mapSettings, QgsProject.instance())
composition.setPaperSize(297, 210)
item = QgsComposerLabel(composition)
composition.addComposerLabel(item)
item.setBackgroundColor(QColor(255, 0, 0))
self.assertEqual(item.backgroundColor(), QColor(255, 0, 0))
self.assertEqual(item.brush().color().name(), QColor(255, 0, 0).name())
item.dataDefinedProperties().setProperty(QgsComposerObject.BackgroundColor, QgsExpressionBasedProperty("'blue'"))
item.refreshDataDefinedProperty()
self.assertEqual(item.backgroundColor(), QColor(255, 0, 0)) # should not change
self.assertEqual(item.brush().color().name(), QColor(0, 0, 255).name())
if __name__ == '__main__':
unittest.main()