[FEATURE] Add background shapes and svg symbols to labels

- Rectangle, square, circle and ellipse generated symbols
- SVG background via new symbology, so uses cache
- Add new gui class QgsSvgSelectorWidget, symbol selector setup culled from symbologyV2
- Does not include any associated data defined mappings, yet
- Does not auto-search for missing SVGs, yet
- Caveat: does not currently account for background size in PAL collision calculations (overlaps occur)
- Allow direct update of QgsColorButton background
This commit is contained in:
Larry Shaffer 2013-03-28 07:43:52 -06:00
parent f832a34fa0
commit b809723977
20 changed files with 3459 additions and 1102 deletions

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 9.0, SVG Export Plug-In -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20000303 Stylable//EN" "http://www.w3.org/TR/2000/03/WD-SVG-20000303/DTD/svg-20000303-stylable.dtd" [
<!ENTITY st0 "fill:param(fill) #ffffff;fill-opacity:1;stroke-width:param(outline-width) 3;">
<!ENTITY st1 "fill-rule:nonzero;clip-rule:nonzero;stroke:param(outline) #000000;stroke-miterlimit:4;stroke-opacity:1;">
]>
<svg width="109pt" height="109.5pt" viewBox="0 0 109 109.5" xml:space="preserve">
<g id="Layer_x0020_1" style="&st1;">
<path style="&st0;" d="M107.5,108H1.5V1.5h106V108z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 657 B

View File

@ -87,6 +87,26 @@ class QgsMapRenderer : QObject
//MAP_UNITS probably supported in future versions
};
/** Blending modes enum defining the available composition modes that can
* be used when rendering a layer
*/
enum BlendMode
{
BlendNormal,
BlendLighten,
BlendScreen,
BlendDodge,
BlendAddition,
BlendDarken,
BlendMultiply,
BlendBurn,
BlendOverlay,
BlendSoftLight,
BlendHardLight,
BlendDifference,
BlendSubtract
};
//! constructor
QgsMapRenderer();

View File

@ -48,6 +48,38 @@ class QgsPalLayerSettings
MultiRight
};
enum ShapeType
{
ShapeRectangle = 0,
ShapeSquare,
ShapeEllipse,
ShapeCircle,
ShapeSVG
};
enum SizeType
{
SizeBuffer = 0,
SizeFixed,
SizePercent
};
enum RotationType
{
RotationSync = 0,
RotationOffset,
RotationFixed
};
/** Units used for option sizes, before being converted to rendered sizes */
enum SizeUnit
{
Points = 0,
MM,
MapUnits,
Percent
};
// update mDataDefinedNames QList in constructor when adding/deleting enum value
enum DataDefinedProperties
{
@ -100,6 +132,7 @@ class QgsPalLayerSettings
QString textNamedStyle;
QColor textColor;
int textTransp;
QPainter::CompositionMode blendMode;
QColor previewBkgrdColor;
bool enabled;
int priority; // 0 = low, 10 = high
@ -111,11 +144,34 @@ class QgsPalLayerSettings
// disabled if both are zero
int scaleMin;
int scaleMax;
double bufferSize; //buffer size (in mm)
double bufferSize; //buffer size
QColor bufferColor;
int bufferTransp;
QPainter::CompositionMode bufferBlendMode;
Qt::PenJoinStyle bufferJoinStyle;
bool bufferNoFill; //set interior of buffer to 100% transparent
// shape background
bool shapeDraw;
ShapeType shapeType;
QString shapeSVGFile;
SizeType shapeSizeType;
QPointF shapeSize;
SizeUnit shapeSizeUnits;
RotationType shapeRotationType;
double shapeRotation;
QPointF shapeOffset;
SizeUnit shapeOffsetUnits;
QPointF shapeRadii;
SizeUnit shapeRadiiUnits;
QColor shapeFillColor;
QColor shapeBorderColor;
double shapeBorderWidth;
SizeUnit shapeBorderWidthUnits;
Qt::PenJoinStyle shapeJoinStyle;
int shapeTransparency;
QPainter::CompositionMode shapeBlendMode;
bool formatNumbers;
int decimals;
bool plusSign;
@ -161,16 +217,18 @@ class QgsPalLayerSettings
void removeDataDefinedProperty( DataDefinedProperties p );
/**Stores field names for data defined layer properties*/
// QMap< DataDefinedProperties, int > dataDefinedProperties;
// QMap< DataDefinedProperties, QString > dataDefinedProperties;
bool preserveRotation; // preserve predefined rotation data during label pin/unpin operations
/**Calculates pixel size (considering output size should be in pixel or map units, scale factors and oversampling)
@param size size to convert
@param c rendercontext
@param buffer whether it buffer size being calculated
@return font pixel size*/
int sizeToPixel( double size, const QgsRenderContext& c , bool buffer = false ) const;
/** Calculates pixel size (considering output size should be in pixel or map units, scale factors and optionally oversampling)
* @param size size to convert
* @param c rendercontext
* @param unit SizeUnit enum value of size
* @param rasterfactor whether to consider oversampling
* @return font pixel size
*/
int sizeToPixel( double size, const QgsRenderContext& c , SizeUnit unit, bool rasterfactor ) const;
/** List of data defined enum names
* @note adding in 1.9
@ -198,6 +256,15 @@ class QgsPalLabeling : QgsLabelingEngineInterface
%End
public:
enum DrawLabelType
{
LabelText = 0,
LabelBuffer,
LabelShape,
LabelSVG,
LabelShadow
};
QgsPalLabeling();
~QgsPalLabeling();
@ -246,9 +313,19 @@ class QgsPalLabeling : QgsLabelingEngineInterface
/*
void drawLabelCandidateRect( pal::LabelPosition* lp, QPainter* painter, const QgsMapToPixel* xform );
//!drawLabel
void drawLabel( pal::LabelPosition* label, QPainter* painter, const QFont& f, const QColor& c, const QgsMapToPixel* xform, double bufferSize = -1,
const QColor& bufferColor = QColor( 255, 255, 255 ), bool drawBuffer = false );
void drawLabel( pal::LabelPosition* label, QgsRenderContext& context, QgsPalLayerSettings& tmplyr, DrawLabelType drawType );
*/
static void drawLabelBuffer( QPainter* p, QString text, const QFont& font, double size, QColor color , Qt::PenJoinStyle joinstyle = Qt::BevelJoin, bool noFill = false );
static void drawLabelBuffer( QgsRenderContext& context, QString text, const QgsPalLayerSettings& tmpLyr );
static void drawLabelBackground( QgsRenderContext& context,
const QgsPoint& centerPt, double labelRotation, double labelWidth, double labelHeight,
const QgsPalLayerSettings& tmpLyr );
//! load/save engine settings to project file
//! @note added in QGIS 1.9
void loadEngineSettings();
void saveEngineSettings();
void clearEngineSettings();
bool isStoredWithProject() const;
void setStoredWithProject( bool store );
};

View File

@ -86,6 +86,17 @@ class QgsColorButton: QPushButton
*/
void setAcceptLiveUpdates( bool accept );
public slots:
/**
* Sets the background pixmap for the button based upon set color and transparency.
* Call directly to update background after adding/removing QColorDialog::ShowAlphaChannel option
* but the color has not changed, i.e. setColor() wouldn't update button and
* you want the button to retain the set color's alpha component regardless
*
* @note added in 1.9
*/
void setButtonBackground();
signals:
/**
* Is emitted, whenever a new color is accepted. The color is always valid.

View File

@ -26,10 +26,14 @@
#include "qgsexpressionbuilderdialog.h"
#include "qgsexpression.h"
#include "qgisapp.h"
#include "qgsmaprenderer.h"
#include "qgsproject.h"
#include "qgssvgcache.h"
#include "qgscharacterselectdialog.h"
#include "qgssvgselectorwidget.h"
#include <QColorDialog>
#include <QFileDialog>
#include <QFontDialog>
#include <QTextEdit>
#include <QApplication>
@ -299,6 +303,52 @@ QgsLabelingGui::QgsLabelingGui( QgsPalLabeling* lbl, QgsVectorLayer* layer, QgsM
updateFontViaStyle( lyr.textNamedStyle );
updateFont( mRefFont );
// shape background
mShapeBackgroundGrpBx->setChecked( lyr.shapeDraw );
mShapeTypeCmbBx->blockSignals( true );
mShapeTypeCmbBx->setCurrentIndex( lyr.shapeType );
mShapeTypeCmbBx->blockSignals( false );
// set up SVG preview
mSvgSelector = new QgsSvgSelectorWidget( this );
mSVGSelectGrpBx->layout()->addWidget( mSvgSelector );
mSvgSelector->setSvgPath( lyr.shapeSVGFile );
mShapeSizeCmbBx->setCurrentIndex( lyr.shapeSizeType );
mShapeSizeXSpnBx->setValue( lyr.shapeSize.x() );
mShapeSizeYSpnBx->setValue( lyr.shapeSize.y() );
mShapeSizeUnitsCmbBx->setCurrentIndex( lyr.shapeSizeUnits - 1 );
mShapeRotationCmbBx->setCurrentIndex( lyr.shapeRotationType );
mShapeRotationDblSpnBx->setEnabled( lyr.shapeRotationType != QgsPalLayerSettings::RotationSync );
mShapeRotationDblSpnBx->setValue( lyr.shapeRotation );
mShapeOffsetXSpnBx->setValue( lyr.shapeOffset.x() );
mShapeOffsetYSpnBx->setValue( lyr.shapeOffset.y() );
mShapeOffsetUnitsCmbBx->setCurrentIndex( lyr.shapeOffsetUnits - 1 );
mShapeRadiusXDbSpnBx->setValue( lyr.shapeRadii.x() );
mShapeRadiusYDbSpnBx->setValue( lyr.shapeRadii.y() );
mShapeRadiusUnitsCmbBx->setCurrentIndex( lyr.shapeRadiiUnits - 1 );
mShapeFillColorBtn->setColor( lyr.shapeFillColor );
mShapeBorderColorBtn->setColor( lyr.shapeBorderColor );
mShapeBorderWidthSpnBx->setValue( lyr.shapeBorderWidth );
mShapeBorderWidthUnitsCmbBx->setCurrentIndex( lyr.shapeBorderWidthUnits - 1 );
mShapePenStyleCmbBx->setPenJoinStyle( lyr.shapeJoinStyle );
connect( mShapeTranspSlider, SIGNAL( valueChanged( int ) ), mShapeTranspSpinBox, SLOT( setValue( int ) ) );
connect( mShapeTranspSpinBox, SIGNAL( valueChanged( int ) ), mShapeTranspSlider, SLOT( setValue( int ) ) );
mShapeTranspSpinBox->setValue( lyr.shapeTransparency );
mShapeBlendCmbBx->setBlendMode( QgsMapRenderer::getBlendModeEnum( lyr.shapeBlendMode ) );
mLoadSvgParams = false;
on_mShapeTypeCmbBx_currentIndexChanged( lyr.shapeType ); // force update of shape background gui
connect( mSvgSelector, SIGNAL( svgSelected( const QString& ) ), this, SLOT( updateSvgWidgets( const QString& ) ) );
mShapeCollisionsChkBx->setVisible( false ); // until implemented
// drop shadow
mShadowGrpBx->setVisible( false ); // until implemented
updateUi();
updateOptions();
@ -448,7 +498,7 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
lyr.textFont = mRefFont;
lyr.textNamedStyle = mFontStyleComboBox->currentText();
lyr.textTransp = mFontTranspSpinBox->value();
lyr.blendMode = ( QgsMapRenderer::BlendMode ) comboBlendMode->blendMode();
lyr.blendMode = QgsMapRenderer::getCompositionMode(( QgsMapRenderer::BlendMode )comboBlendMode->blendMode() );
lyr.previewBkgrdColor = mPreviewBackgroundBtn->color();
lyr.enabled = chkEnableLabeling->isChecked();
lyr.priority = sliderPriority->value();
@ -473,12 +523,37 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
lyr.bufferSizeInMapUnits = ( mBufferUnitComboBox->currentIndex() == 1 );
lyr.bufferJoinStyle = mBufferJoinStyleComboBox->penJoinStyle();
lyr.bufferNoFill = !mBufferTranspFillChbx->isChecked();
lyr.bufferBlendMode = ( QgsMapRenderer::BlendMode ) comboBufferBlendMode->blendMode();
lyr.bufferBlendMode = QgsMapRenderer::getCompositionMode(( QgsMapRenderer::BlendMode )comboBufferBlendMode->blendMode() );
}
else
{
lyr.bufferSize = 0;
}
// shape background
lyr.shapeDraw = mShapeBackgroundGrpBx->isChecked();
lyr.shapeType = ( QgsPalLayerSettings::ShapeType )mShapeTypeCmbBx->currentIndex();
lyr.shapeSVGFile = mSvgSelector->currentSvgPath();
lyr.shapeSizeType = ( QgsPalLayerSettings::SizeType )mShapeSizeCmbBx->currentIndex();
lyr.shapeSize = QPointF( mShapeSizeXSpnBx->value(), mShapeSizeYSpnBx->value() );
lyr.shapeSizeUnits = ( QgsPalLayerSettings::SizeUnit )( mShapeSizeUnitsCmbBx->currentIndex() + 1 );
lyr.shapeRotationType = ( QgsPalLayerSettings::RotationType )( mShapeRotationCmbBx->currentIndex() );
lyr.shapeRotation = mShapeRotationDblSpnBx->value();
lyr.shapeOffset = QPointF( mShapeOffsetXSpnBx->value(), mShapeOffsetYSpnBx->value() );
lyr.shapeOffsetUnits = ( QgsPalLayerSettings::SizeUnit )( mShapeOffsetUnitsCmbBx->currentIndex() + 1 );
lyr.shapeRadii = QPointF( mShapeRadiusXDbSpnBx->value(), mShapeRadiusYDbSpnBx->value() );
lyr.shapeRadiiUnits = ( QgsPalLayerSettings::SizeUnit )( mShapeRadiusUnitsCmbBx->currentIndex() + 1 );
lyr.shapeFillColor = mShapeFillColorBtn->color();
lyr.shapeBorderColor = mShapeBorderColorBtn->color();
lyr.shapeBorderWidth = mShapeBorderWidthSpnBx->value();
lyr.shapeBorderWidthUnits = ( QgsPalLayerSettings::SizeUnit )( mShapeBorderWidthUnitsCmbBx->currentIndex() + 1 );
lyr.shapeJoinStyle = mShapePenStyleCmbBx->penJoinStyle();
lyr.shapeTransparency = mShapeTranspSpinBox->value();
lyr.shapeBlendMode = QgsMapRenderer::getCompositionMode(( QgsMapRenderer::BlendMode )mShapeBlendCmbBx->blendMode() );
if ( chkFormattedNumbers->isChecked() )
{
lyr.formatNumbers = true;
@ -821,13 +896,14 @@ void QgsLabelingGui::updatePreview()
double previewRatio = mPreviewSize / fontSize;
double bufferSize = 0.0;
QString grpboxtitle;
QString sampleTxt = tr( "Text/Buffer sample" );
if ( mFontSizeUnitComboBox->currentIndex() == 1 ) // map units
{
// TODO: maybe match current map zoom level instead?
previewFont.setPointSize( mPreviewSize );
mPreviewSizeSlider->setEnabled( true );
grpboxtitle = tr( "Sample @ %1 pts (using map units)" ).arg( mPreviewSize );
grpboxtitle = sampleTxt + tr( " @ %1 pts (using map units)" ).arg( mPreviewSize );
previewFont.setWordSpacing( previewRatio * mFontWordSpacingSpinBox->value() );
previewFont.setLetterSpacing( QFont::AbsoluteSpacing, previewRatio * mFontLetterSpacingSpinBox->value() );
@ -840,7 +916,7 @@ void QgsLabelingGui::updatePreview()
}
else // millimeters
{
grpboxtitle = tr( "Sample @ %1 pts (using map units, BUFFER IN MILLIMETERS)" ).arg( mPreviewSize );
grpboxtitle = sampleTxt + tr( " @ %1 pts (using map units, BUFFER IN MILLIMETERS)" ).arg( mPreviewSize );
bufferSize = spinBufferSize->value();
}
}
@ -849,7 +925,7 @@ void QgsLabelingGui::updatePreview()
{
previewFont.setPointSize( fontSize );
mPreviewSizeSlider->setEnabled( false );
grpboxtitle = tr( "Sample" );
grpboxtitle = sampleTxt;
if ( chkBuffer->isChecked() )
{
@ -859,7 +935,7 @@ void QgsLabelingGui::updatePreview()
}
else // map units
{
grpboxtitle = tr( "Sample (BUFFER NOT SHOWN, in map units)" );
grpboxtitle = sampleTxt + tr( " (BUFFER NOT SHOWN, in map units)" );
}
}
}
@ -1134,6 +1210,110 @@ void QgsLabelingGui::on_mYCoordinateComboBox_currentIndexChanged( const QString
}
}
void QgsLabelingGui::on_mShapeTypeCmbBx_currentIndexChanged( int index )
{
// shape background
bool isRect = (( QgsPalLayerSettings::ShapeType )index == QgsPalLayerSettings::ShapeRectangle
|| ( QgsPalLayerSettings::ShapeType )index == QgsPalLayerSettings::ShapeSquare );
bool isSVG = (( QgsPalLayerSettings::ShapeType )index == QgsPalLayerSettings::ShapeSVG );
mShapePenStyleLine->setVisible( isRect );
mShapePenStyleLabel->setVisible( isRect );
mShapePenStyleCmbBx->setVisible( isRect );
mShapeRadiusLabel->setVisible( isRect );
mShapeRadiusFrame->setVisible( isRect );
mSvgSelector->setMinimumHeight( isSVG ? 240 : 0 );
mSVGSelectGrpBx->setVisible( isSVG );
// mSVGSelectGrpBx->setCollapsed( !isSVG );
// symbology SVG renderer only supports size^2 scaling, so we only use the x size spinbox
mShapeSizeYSpnBx->setVisible( !isSVG );
// SVG parameter setting doesn't support color's alpha component yet
mShapeFillColorBtn->setColorDialogOptions( isSVG ? QColorDialog::ColorDialogOptions( 0 ) : QColorDialog::ShowAlphaChannel );
mShapeFillColorBtn->setButtonBackground();
mShapeBorderColorBtn->setColorDialogOptions( isSVG ? QColorDialog::ColorDialogOptions( 0 ) : QColorDialog::ShowAlphaChannel );
mShapeBorderColorBtn->setButtonBackground();
// configure SVG parameter widgets
mShapeSVGParamsBtn->setVisible( isSVG );
QString svgPath = mSvgSelector->currentSvgPath();
if ( isSVG )
{
mShapePenStyleLine->setVisible( true );
updateSvgWidgets( svgPath );
}
else
{
mShapeFillColorLabel->setEnabled( true );
mShapeFillColorBtn->setEnabled( true );
mShapeBorderColorLabel->setEnabled( true );
mShapeBorderColorBtn->setEnabled( true );
mShapeBorderWidthLabel->setEnabled( true );
mShapeBorderWidthSpnBx->setEnabled( true );
}
// TODO: fix overriding SVG symbol's border width units in QgsSvgCache
// currently broken, fall back to symbol's
mShapeBorderWidthUnitsCmbBx->setVisible( !isSVG );
mShapeSVGUnitsLabel->setVisible( isSVG );
}
void QgsLabelingGui::updateSvgWidgets( const QString& svgPath )
{
bool validSVG = false;
QFileInfo finfo( svgPath );
validSVG = finfo.exists();
if ( !validSVG )
validSVG = ( QUrl( svgPath ).isValid() );
QString grpbxTitle = tr( "Select SVG symbol" );
mSVGSelectGrpBx->setTitle( validSVG ? grpbxTitle + ": " + finfo.fileName() : grpbxTitle );
QColor fill, outline;
double outlineWidth = 0.0;
bool fillParam = false, outlineParam = false, outlineWidthParam = false;
if ( validSVG )
{
QgsSvgCache::instance()->containsParams( svgPath, fillParam, fill, outlineParam, outline, outlineWidthParam, outlineWidth );
}
mShapeSVGParamsBtn->setEnabled( validSVG && ( fillParam || outlineParam || outlineWidthParam ) );
mShapeFillColorLabel->setEnabled( validSVG && fillParam );
mShapeFillColorBtn->setEnabled( validSVG && fillParam );
if ( mLoadSvgParams && validSVG && fillParam )
mShapeFillColorBtn->setColor( fill );
mShapeBorderColorLabel->setEnabled( validSVG && outlineParam );
mShapeBorderColorBtn->setEnabled( validSVG && outlineParam );
if ( mLoadSvgParams && validSVG && outlineParam )
mShapeBorderColorBtn->setColor( outline );
mShapeBorderWidthLabel->setEnabled( validSVG && outlineWidthParam );
mShapeBorderWidthSpnBx->setEnabled( validSVG && outlineWidthParam );
if ( mLoadSvgParams && validSVG && outlineWidthParam )
mShapeBorderWidthSpnBx->setValue( outlineWidth );
// TODO: fix overriding SVG symbol's border width units in QgsSvgCache
// currently broken, fall back to symbol's
//mShapeBorderWidthUnitsCmbBx->setEnabled( validSVG && outlineWidthParam );
mShapeSVGUnitsLabel->setEnabled( validSVG && outlineWidthParam );
}
void QgsLabelingGui::on_mShapeSVGParamsBtn_clicked()
{
QString svgPath = mSvgSelector->currentSvgPath();
mLoadSvgParams = true;
updateSvgWidgets( svgPath );
mLoadSvgParams = false;
}
void QgsLabelingGui::on_mShapeRotationCmbBx_currentIndexChanged( int index )
{
mShapeRotationDblSpnBx->setEnabled(( QgsPalLayerSettings::RotationType )index != QgsPalLayerSettings::RotationSync );
}
void QgsLabelingGui::on_mPreviewTextEdit_textChanged( const QString & text )
{
lblFontPreview->setText( text );

View File

@ -25,6 +25,7 @@
class QgsVectorLayer;
class QgsMapCanvas;
class QgsCharacterSelectorDialog;
class QgsSvgSelectorWidget;
#include "qgspallabeling.h"
@ -53,6 +54,7 @@ class QgsLabelingGui : public QWidget, private Ui::QgsLabelingGuiBase
void scrollPreview();
void updateOptions();
void updateQuadrant();
void updateSvgWidgets( const QString& svgPath );
void on_mPreviewSizeSlider_valueChanged( int i );
void on_mFontSizeSpinBox_valueChanged( double d );
@ -69,6 +71,10 @@ class QgsLabelingGui : public QWidget, private Ui::QgsLabelingGuiBase
void on_mXCoordinateComboBox_currentIndexChanged( const QString & text );
void on_mYCoordinateComboBox_currentIndexChanged( const QString & text );
void on_mShapeTypeCmbBx_currentIndexChanged( int index );
void on_mShapeRotationCmbBx_currentIndexChanged( int index );
void on_mShapeSVGParamsBtn_clicked();
void on_mPreviewTextEdit_textChanged( const QString & text );
void on_mPreviewTextBtn_clicked();
void on_mPreviewBackgroundBtn_colorChanged( const QColor &color );
@ -95,6 +101,7 @@ class QgsLabelingGui : public QWidget, private Ui::QgsLabelingGuiBase
QgsMapCanvas* mMapCanvas;
QFontDatabase mFontDB;
QgsCharacterSelectorDialog* mCharDlg;
QgsSvgSelectorWidget* mSvgSelector;
// background reference font
QFont mRefFont;
@ -104,6 +111,8 @@ class QgsLabelingGui : public QWidget, private Ui::QgsLabelingGuiBase
int mYQuadOffset;
int mMinPixelLimit;
bool mLoadSvgParams;
void disableDataDefinedAlignment();
void enableDataDefinedAlignment();
};

View File

@ -22,6 +22,19 @@
QgsLabelPreview::QgsLabelPreview( QWidget* parent )
: QLabel( parent )
{
mTmpLyr = new QgsPalLayerSettings();
// construct a device-based render context
QgsMapToPixel newCoordXForm;
newCoordXForm.setParameters( 0, 0, 0, 0 );
mContext = new QgsRenderContext();
mContext->setMapToPixel( newCoordXForm );
}
QgsLabelPreview::~QgsLabelPreview()
{
delete mTmpLyr;
delete mContext;
}
void QgsLabelPreview::setTextColor( QColor color )
@ -32,10 +45,13 @@ void QgsLabelPreview::setTextColor( QColor color )
void QgsLabelPreview::setBuffer( double size, QColor color, Qt::PenJoinStyle joinStyle, bool noFill )
{
mBufferSize = size * 88 / 25.4; //assume standard dpi for preview
mBufferColor = color;
mBufferJoinStyle = joinStyle;
mBufferNoFill = noFill;
mTmpLyr->bufferSize = size * 88 / 25.4; //assume standard dpi for preview;
mTmpLyr->bufferSizeInMapUnits = false;
mTmpLyr->bufferColor = color;
mTmpLyr->bufferJoinStyle = joinStyle;
mTmpLyr->bufferNoFill = noFill;
mTmpLyr->textFont = font();
update();
}
@ -49,17 +65,20 @@ void QgsLabelPreview::paintEvent( QPaintEvent *e )
QFontMetrics fm( font() );
// otherwise thin buffers don't look like those on canvas
if ( mBufferSize != 0 && mBufferSize < 1 )
mBufferSize = 1;
if ( mTmpLyr->bufferSize != 0 && mTmpLyr->bufferSize < 1 )
mTmpLyr->bufferSize = 1;
double xtrans = 0;
if ( mBufferSize != 0 )
xtrans = mBufferSize / 4;
if ( mTmpLyr->bufferSize != 0 )
xtrans = mTmpLyr->bufferSize / 4;
p.translate( xtrans, fm.ascent() + 4 );
if ( mBufferSize != 0 )
QgsPalLabeling::drawLabelBuffer( &p, text(), font(), mBufferSize, mBufferColor, mBufferJoinStyle, mBufferNoFill );
if ( mTmpLyr->bufferSize != 0 )
{
mContext->setPainter( &p );
QgsPalLabeling::drawLabelBuffer( *mContext, text(), *mTmpLyr );
}
QPainterPath path;
path.addText( 0, 0, font(), text() );

View File

@ -15,12 +15,17 @@
#ifndef QGSLABELPREVIEW_H
#define QGSLABELPREVIEW_H
#include "qgspallabeling.h"
#include <QLabel>
class QgsRenderContext;
class QgsLabelPreview : public QLabel
{
public:
QgsLabelPreview( QWidget* parent = NULL );
~QgsLabelPreview();
void setTextColor( QColor color );
@ -32,12 +37,12 @@ class QgsLabelPreview : public QLabel
void paintEvent( QPaintEvent* e );
private:
double mBufferSize;
QColor mBufferColor;
Qt::PenJoinStyle mBufferJoinStyle;
bool mBufferNoFill;
QgsPalLayerSettings* mTmpLyr;
QColor mTextColor;
QFont mFont;
// device-based render context
QgsRenderContext* mContext;
};
#endif // LABELPREVIEW_H

View File

@ -260,7 +260,8 @@ QFont QgsMapToolLabel::labelFontCurrentFeature()
{
if ( layerSettings.fontSizeInMapUnits )
{
font.setPixelSize( layerSettings.sizeToPixel( f.attribute( *sizeIt ).toDouble(), QgsRenderContext() ) );
font.setPixelSize( layerSettings.sizeToPixel( f.attribute( *sizeIt ).toDouble(),
QgsRenderContext(), QgsPalLayerSettings::MapUnits, true ) );
}
else
{

View File

@ -1191,4 +1191,40 @@ QPainter::CompositionMode QgsMapRenderer::getCompositionMode( const QgsMapRender
}
}
QgsMapRenderer::BlendMode QgsMapRenderer::getBlendModeEnum( const QPainter::CompositionMode blendMode )
{
// Map QPainter::CompositionMode to QgsMapRenderer::BlendNormal
switch ( blendMode )
{
case QPainter::CompositionMode_SourceOver:
return QgsMapRenderer::BlendNormal;
case QPainter::CompositionMode_Lighten:
return QgsMapRenderer::BlendLighten;
case QPainter::CompositionMode_Screen:
return QgsMapRenderer::BlendScreen;
case QPainter::CompositionMode_ColorDodge:
return QgsMapRenderer::BlendDodge;
case QPainter::CompositionMode_Plus:
return QgsMapRenderer::BlendAddition;
case QPainter::CompositionMode_Darken:
return QgsMapRenderer::BlendDarken;
case QPainter::CompositionMode_Multiply:
return QgsMapRenderer::BlendMultiply;
case QPainter::CompositionMode_ColorBurn:
return QgsMapRenderer::BlendBurn;
case QPainter::CompositionMode_Overlay:
return QgsMapRenderer::BlendOverlay;
case QPainter::CompositionMode_SoftLight:
return QgsMapRenderer::BlendSoftLight;
case QPainter::CompositionMode_HardLight:
return QgsMapRenderer::BlendHardLight;
case QPainter::CompositionMode_Difference:
return QgsMapRenderer::BlendDifference;
case QPainter::CompositionMode_Exclusion:
return QgsMapRenderer::BlendSubtract;
default:
return QgsMapRenderer::BlendNormal;
}
}
bool QgsMapRenderer::mDrawing = false;

View File

@ -252,7 +252,11 @@ class CORE_EXPORT QgsMapRenderer : public QObject
void setLabelingEngine( QgsLabelingEngineInterface* iface );
//! Returns a QPainter::CompositionMode corresponding to a BlendMode
//! Added in 1.9
static QPainter::CompositionMode getCompositionMode( const QgsMapRenderer::BlendMode blendMode );
//! Returns a BlendMode corresponding to a QPainter::CompositionMode
//! Added in 1.9
static QgsMapRenderer::BlendMode getBlendModeEnum( const QPainter::CompositionMode blendMode );
signals:
@ -347,7 +351,6 @@ class CORE_EXPORT QgsMapRenderer : public QObject
private:
const QgsCoordinateTransform* tr( QgsMapLayer *layer );
};
#endif

View File

@ -49,7 +49,10 @@
#include <qgsvectordataprovider.h>
#include <qgsgeometry.h>
#include <qgsmaprenderer.h>
#include <qgsmarkersymbollayerv2.h>
#include <qgsproject.h>
#include "qgssymbolv2.h"
#include "qgssymbollayerv2utils.h"
#include <QMessageBox>
using namespace pal;
@ -202,7 +205,7 @@ QgsPalLayerSettings::QgsPalLayerSettings()
textNamedStyle = QString( "" );
textColor = Qt::black;
textTransp = 0;
blendMode = QgsMapRenderer::BlendNormal;
blendMode = QPainter::CompositionMode_SourceOver;
previewBkgrdColor = Qt::white;
enabled = false;
priority = 5;
@ -213,7 +216,7 @@ QgsPalLayerSettings::QgsPalLayerSettings()
bufferSize = 1;
bufferColor = Qt::white;
bufferTransp = 0;
bufferBlendMode = QgsMapRenderer::BlendNormal;
bufferBlendMode = QPainter::CompositionMode_SourceOver;
bufferNoFill = false;
bufferJoinStyle = Qt::BevelJoin;
formatNumbers = false;
@ -247,6 +250,27 @@ QgsPalLayerSettings::QgsPalLayerSettings()
multilineAlign = MultiLeft;
preserveRotation = true;
// shape background
shapeDraw = false;
shapeType = ShapeRectangle;
shapeSVGFile = QString();
shapeSizeType = SizeBuffer;
shapeSize = QPointF( 0.0, 0.0 );
shapeSizeUnits = MM;
shapeRotationType = RotationSync;
shapeRotation = 0.0;
shapeOffset = QPointF( 0.0, 0.0 );
shapeOffsetUnits = MM;
shapeRadii = QPointF( 0.0, 0.0 );
shapeRadiiUnits = MM;
shapeFillColor = Qt::white;
shapeBorderColor = Qt::darkGray;
shapeBorderWidth = 0.0;
shapeBorderWidthUnits = MM;
shapeJoinStyle = Qt::BevelJoin;
shapeTransparency = 0;
shapeBlendMode = QPainter::CompositionMode_SourceOver;
// data defined string values
mDataDefinedNames << "Size";
mDataDefinedNames << "Bold";
@ -269,6 +293,8 @@ QgsPalLayerSettings::QgsPalLayerSettings()
mDataDefinedNames << "FontTransp";
mDataDefinedNames << "BufferTransp";
mDataDefinedNames << "AlwaysShow";
// temp stuff for drawing different label types (don't copy)
}
QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
@ -333,6 +359,28 @@ QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
multilineAlign = s.multilineAlign;
preserveRotation = s.preserveRotation;
// shape background
shapeDraw = s.shapeDraw;
shapeType = s.shapeType;
shapeSVGFile = s.shapeSVGFile;
shapeSizeType = s.shapeSizeType;
shapeSize = s.shapeSize;
shapeSizeUnits = s.shapeSizeUnits;
shapeRotationType = s.shapeRotationType;
shapeRotation = s.shapeRotation;
shapeOffset = s.shapeOffset;
shapeOffsetUnits = s.shapeOffsetUnits;
shapeRadii = s.shapeRadii;
shapeRadiiUnits = s.shapeRadiiUnits;
shapeFillColor = s.shapeFillColor;
shapeBorderColor = s.shapeBorderColor;
shapeBorderWidth = s.shapeBorderWidth;
shapeBorderWidthUnits = s.shapeBorderWidthUnits;
shapeJoinStyle = s.shapeJoinStyle;
shapeTransparency = s.shapeTransparency;
shapeBlendMode = s.shapeBlendMode;
dataDefinedProperties = s.dataDefinedProperties;
mDataDefinedNames = s.mDataDefinedNames;
@ -360,19 +408,22 @@ QgsExpression* QgsPalLayerSettings::getLabelExpression()
return expression;
}
static QColor _readColor( QgsVectorLayer* layer, QString property )
static QColor _readColor( QgsVectorLayer* layer, QString property, QColor defaultColor = Qt::black, bool withAlpha = true )
{
int r = layer->customProperty( property + "R" ).toInt();
int g = layer->customProperty( property + "G" ).toInt();
int b = layer->customProperty( property + "B" ).toInt();
return QColor( r, g, b );
int r = layer->customProperty( property + "R", QVariant( defaultColor.red() ) ).toInt();
int g = layer->customProperty( property + "G", QVariant( defaultColor.green() ) ).toInt();
int b = layer->customProperty( property + "B", QVariant( defaultColor.blue() ) ).toInt();
int a = withAlpha ? layer->customProperty( property + "A", QVariant( defaultColor.alpha() ) ).toInt() : 255;
return QColor( r, g, b, a );
}
static void _writeColor( QgsVectorLayer* layer, QString property, QColor color )
static void _writeColor( QgsVectorLayer* layer, QString property, QColor color, bool withAlpha = true )
{
layer->setCustomProperty( property + "R", color.red() );
layer->setCustomProperty( property + "G", color.green() );
layer->setCustomProperty( property + "B", color.blue() );
if ( withAlpha )
layer->setCustomProperty( property + "A", color.alpha() );
}
void QgsPalLayerSettings::readDataDefinedPropertyMap( QgsVectorLayer* layer,
@ -526,9 +577,10 @@ void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer )
textFont.setStrikeOut( layer->customProperty( "labeling/fontStrikeout" ).toBool() );
textFont.setLetterSpacing( QFont::AbsoluteSpacing, layer->customProperty( "labeling/fontLetterSpacing", QVariant( 0.0 ) ).toDouble() );
textFont.setWordSpacing( layer->customProperty( "labeling/fontWordSpacing", QVariant( 0.0 ) ).toDouble() );
textColor = _readColor( layer, "labeling/textColor" );
textColor = _readColor( layer, "labeling/textColor", Qt::black, false );
textTransp = layer->customProperty( "labeling/textTransp" ).toInt();
blendMode = ( QgsMapRenderer::BlendMode ) layer->customProperty( "labeling/blendMode" ).toInt();
blendMode = QgsMapRenderer::getCompositionMode(
( QgsMapRenderer::BlendMode )layer->customProperty( "labeling/blendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() );
previewBkgrdColor = QColor( layer->customProperty( "labeling/previewBkgrdColor", QVariant( "#ffffff" ) ).toString() );
enabled = layer->customProperty( "labeling/enabled" ).toBool();
priority = layer->customProperty( "labeling/priority" ).toInt();
@ -537,9 +589,10 @@ void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer )
scaleMin = layer->customProperty( "labeling/scaleMin" ).toInt();
scaleMax = layer->customProperty( "labeling/scaleMax" ).toInt();
bufferSize = layer->customProperty( "labeling/bufferSize" ).toDouble();
bufferColor = _readColor( layer, "labeling/bufferColor" );
bufferColor = _readColor( layer, "labeling/bufferColor", Qt::white, false );
bufferTransp = layer->customProperty( "labeling/bufferTransp" ).toInt();
bufferBlendMode = ( QgsMapRenderer::BlendMode ) layer->customProperty( "labeling/bufferBlendMode" ).toInt();
bufferBlendMode = QgsMapRenderer::getCompositionMode(
( QgsMapRenderer::BlendMode )layer->customProperty( "labeling/bufferBlendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() );
bufferJoinStyle = ( Qt::PenJoinStyle ) layer->customProperty( "labeling/bufferJoinStyle", QVariant( Qt::BevelJoin ) ).toUInt();
bufferNoFill = layer->customProperty( "labeling/bufferNoFill", QVariant( false ) ).toBool();
formatNumbers = layer->customProperty( "labeling/formatNumbers" ).toBool();
@ -570,6 +623,32 @@ void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer )
multilineHeight = layer->customProperty( "labeling/multilineHeight", QVariant( 1.0 ) ).toDouble();
multilineAlign = ( MultiLineAlign ) layer->customProperty( "labeling/multilineAlign", QVariant( MultiLeft ) ).toUInt();
preserveRotation = layer->customProperty( "labeling/preserveRotation", QVariant( true ) ).toBool();
// shape background
shapeDraw = layer->customProperty( "labeling/shapeDraw", QVariant( false ) ).toBool();
shapeType = ( ShapeType ) layer->customProperty( "labeling/shapeType", QVariant( ShapeRectangle ) ).toUInt();
shapeSVGFile = layer->customProperty( "labeling/shapeSVGFile", QVariant( "" ) ).toString();
shapeSizeType = ( SizeType ) layer->customProperty( "labeling/shapeSizeType", QVariant( SizeBuffer ) ).toUInt();
shapeSize = QPointF( layer->customProperty( "labeling/shapeSizeX", QVariant( 0.0 ) ).toDouble(),
layer->customProperty( "labeling/shapeSizeY", QVariant( 0.0 ) ).toDouble() );
shapeSizeUnits = ( SizeUnit )layer->customProperty( "labeling/shapeSizeUnits", QVariant( MM ) ).toUInt();
shapeRotationType = ( RotationType ) layer->customProperty( "labeling/shapeRotationType", QVariant( RotationSync ) ).toUInt();
shapeRotation = layer->customProperty( "labeling/shapeRotation", QVariant( 0.0 ) ).toDouble();
shapeOffset = QPointF( layer->customProperty( "labeling/shapeOffsetX", QVariant( 0.0 ) ).toDouble(),
layer->customProperty( "labeling/shapeOffsetY", QVariant( 0.0 ) ).toDouble() );
shapeOffsetUnits = ( SizeUnit )layer->customProperty( "labeling/shapeOffsetUnits", QVariant( MM ) ).toUInt();
shapeRadii = QPointF( layer->customProperty( "labeling/shapeRadiiX", QVariant( 0.0 ) ).toDouble(),
layer->customProperty( "labeling/shapeRadiiY", QVariant( 0.0 ) ).toDouble() );
shapeRadiiUnits = ( SizeUnit )layer->customProperty( "labeling/shapeRadiiUnits", QVariant( MM ) ).toUInt();
shapeFillColor = _readColor( layer, "labeling/shapeFillColor", Qt::white, true );
shapeBorderColor = _readColor( layer, "labeling/shapeBorderColor", Qt::darkGray, true );
shapeBorderWidth = layer->customProperty( "labeling/shapeBorderWidth", QVariant( .0 ) ).toDouble();
shapeBorderWidthUnits = ( SizeUnit )layer->customProperty( "labeling/shapeBorderWidthUnits", QVariant( MM ) ).toUInt();
shapeJoinStyle = ( Qt::PenJoinStyle ) layer->customProperty( "labeling/shapeJoinStyle", QVariant( Qt::BevelJoin ) ).toUInt();
shapeTransparency = layer->customProperty( "labeling/shapeTransparency", QVariant( 0 ) ).toInt();
shapeBlendMode = QgsMapRenderer::getCompositionMode(
( QgsMapRenderer::BlendMode )layer->customProperty( "labeling/shapeBlendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() );
readDataDefinedPropertyMap( layer, dataDefinedProperties );
}
@ -602,7 +681,7 @@ void QgsPalLayerSettings::writeToLayer( QgsVectorLayer* layer )
_writeColor( layer, "labeling/textColor", textColor );
layer->setCustomProperty( "labeling/textTransp", textTransp );
layer->setCustomProperty( "labeling/blendMode", ( unsigned int )blendMode );
layer->setCustomProperty( "labeling/blendMode", QgsMapRenderer::getBlendModeEnum( blendMode ) );
layer->setCustomProperty( "labeling/previewBkgrdColor", previewBkgrdColor.name() );
layer->setCustomProperty( "labeling/enabled", enabled );
layer->setCustomProperty( "labeling/priority", priority );
@ -613,7 +692,7 @@ void QgsPalLayerSettings::writeToLayer( QgsVectorLayer* layer )
layer->setCustomProperty( "labeling/bufferSize", bufferSize );
_writeColor( layer, "labeling/bufferColor", bufferColor );
layer->setCustomProperty( "labeling/bufferTransp", bufferTransp );
layer->setCustomProperty( "labeling/bufferBlendMode", ( unsigned int )bufferBlendMode );
layer->setCustomProperty( "labeling/bufferBlendMode", QgsMapRenderer::getBlendModeEnum( bufferBlendMode ) );
layer->setCustomProperty( "labeling/bufferJoinStyle", ( unsigned int )bufferJoinStyle );
layer->setCustomProperty( "labeling/bufferNoFill", bufferNoFill );
layer->setCustomProperty( "labeling/formatNumbers", formatNumbers );
@ -644,6 +723,31 @@ void QgsPalLayerSettings::writeToLayer( QgsVectorLayer* layer )
layer->setCustomProperty( "labeling/multilineHeight", multilineHeight );
layer->setCustomProperty( "labeling/multilineAlign", ( unsigned int )multilineAlign );
layer->setCustomProperty( "labeling/preserveRotation", preserveRotation );
// shape background
layer->setCustomProperty( "labeling/shapeDraw", shapeDraw );
layer->setCustomProperty( "labeling/shapeType", ( unsigned int )shapeType );
layer->setCustomProperty( "labeling/shapeSVGFile", shapeSVGFile );
layer->setCustomProperty( "labeling/shapeSizeType", ( unsigned int )shapeSizeType );
layer->setCustomProperty( "labeling/shapeSizeX", shapeSize.x() );
layer->setCustomProperty( "labeling/shapeSizeY", shapeSize.y() );
layer->setCustomProperty( "labeling/shapeSizeUnits", ( unsigned int )shapeSizeUnits );
layer->setCustomProperty( "labeling/shapeRotationType", ( unsigned int )shapeRotationType );
layer->setCustomProperty( "labeling/shapeRotation", shapeRotation );
layer->setCustomProperty( "labeling/shapeOffsetX", shapeOffset.x() );
layer->setCustomProperty( "labeling/shapeOffsetY", shapeOffset.y() );
layer->setCustomProperty( "labeling/shapeOffsetUnits", ( unsigned int )shapeOffsetUnits );
layer->setCustomProperty( "labeling/shapeRadiiX", shapeRadii.x() );
layer->setCustomProperty( "labeling/shapeRadiiY", shapeRadii.y() );
layer->setCustomProperty( "labeling/shapeRadiiUnits", ( unsigned int )shapeRadiiUnits );
_writeColor( layer, "labeling/shapeFillColor", shapeFillColor, true );
_writeColor( layer, "labeling/shapeBorderColor", shapeBorderColor, true );
layer->setCustomProperty( "labeling/shapeBorderWidth", shapeBorderWidth );
layer->setCustomProperty( "labeling/shapeBorderWidthUnits", ( unsigned int )shapeBorderWidthUnits );
layer->setCustomProperty( "labeling/shapeJoinStyle", ( unsigned int )shapeJoinStyle );
layer->setCustomProperty( "labeling/shapeTransparency", shapeTransparency );
layer->setCustomProperty( "labeling/shapeBlendMode", QgsMapRenderer::getBlendModeEnum( shapeBlendMode ) );
writeDataDefinedPropertyMap( layer, dataDefinedProperties );
}
@ -719,7 +823,7 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString t
}
else
{
text.append( dirSym + wrapchr ); // SymbolAbove or SymbolBelow
text.prepend( dirSym + wrapchr ); // SymbolAbove or SymbolBelow
}
}
@ -808,11 +912,11 @@ void QgsPalLayerSettings::registerFeature( QgsVectorLayer* layer, QgsFeature& f
if ( size.isValid() )
{
double sizeDouble = size.toDouble();
if ( sizeDouble <= 0.0 || sizeToPixel( sizeDouble, context ) < 1 )
if ( sizeDouble <= 0.0 || sizeToPixel( sizeDouble, context, fontSizeInMapUnits ? MapUnits : Points, true ) < 1 )
{
return;
}
labelFont.setPixelSize( sizeToPixel( sizeDouble, context ) );
labelFont.setPixelSize( sizeToPixel( sizeDouble, context, fontSizeInMapUnits ? MapUnits : Points, true ) );
}
}
@ -965,21 +1069,22 @@ void QgsPalLayerSettings::registerFeature( QgsVectorLayer* layer, QgsFeature& f
}
// adjust offset of labels to match chosen unit and map scale
// offsets match those of symbology: -x = left, -y = up
double mapUntsPerMM = context.mapToPixel().mapUnitsPerPixel() * context.scaleFactor();
if ( xOffset != 0 )
{
offsetX = xOffset;
offsetX = xOffset; // must be positive to match symbology offset direction
if ( !labelOffsetInMapUnits )
{
offsetX = xOffset * mapUntsPerMM; //convert offset from mm to map units
offsetX *= mapUntsPerMM; //convert offset from mm to map units
}
}
if ( yOffset != 0 )
{
offsetY = yOffset;
offsetY = -yOffset; // must be negative to match symbology offset direction
if ( !labelOffsetInMapUnits )
{
offsetY = yOffset * mapUntsPerMM; //convert offset from mm to map units
offsetY *= mapUntsPerMM; //convert offset from mm to map units
}
}
@ -1174,23 +1279,27 @@ void QgsPalLayerSettings::registerFeature( QgsVectorLayer* layer, QgsFeature& f
lbl->setIsPinned( labelIsPinned );
}
int QgsPalLayerSettings::sizeToPixel( double size, const QgsRenderContext& c, bool buffer ) const
int QgsPalLayerSettings::sizeToPixel( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor ) const
{
double pixelSize;
if (( !buffer && fontSizeInMapUnits ) || ( buffer && bufferSizeInMapUnits ) )
// if render context is that of device (i.e. not a scaled map), just return rounded size
double pixelSize = size;
double mapUnitsPerPixel = c.mapToPixel().mapUnitsPerPixel();
if ( mapUnitsPerPixel > 0.0 )
{
pixelSize = size / c.mapToPixel().mapUnitsPerPixel() * c.rasterScaleFactor();
}
else //font size in points, or buffer in mm
{
double ptsTomm = buffer ? 1 : 0.3527;
// set font size from points to output size
pixelSize = ptsTomm * size * c.scaleFactor() * c.rasterScaleFactor();
if ( unit == MapUnits )
{
pixelSize = size / mapUnitsPerPixel * ( rasterfactor ? c.rasterScaleFactor() : 1 );
}
else // e.g. in points or mm
{
double ptsTomm = ( unit == Points ? 0.352778 : 1 );
pixelSize = ptsTomm * size * c.scaleFactor() * ( rasterfactor ? c.rasterScaleFactor() : 1 );
}
}
return ( int )( pixelSize + 0.5 );
}
// -------------
QgsPalLabeling::QgsPalLabeling()
@ -1331,7 +1440,9 @@ int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QSet<int>& attrIndices,
l->setUpsidedownLabels( upsdnlabels );
// fix for font size in map units causing font to show pointsize at small map scales
int pixelFontSize = lyr.sizeToPixel( lyr.textFont.pointSizeF(), ctx );
int pixelFontSize = lyr.sizeToPixel( lyr.textFont.pointSizeF(), ctx,
lyr.fontSizeInMapUnits ? QgsPalLayerSettings::MapUnits : QgsPalLayerSettings::Points,
true );
if ( pixelFontSize < 1 )
{
@ -1650,30 +1761,29 @@ void QgsPalLabeling::drawLabeling( QgsRenderContext& context )
}
const QgsPalLayerSettings& lyr = layer( layerNameUtf8 );
QFont fontForLabel = lyr.textFont;
QColor fontColor = lyr.textColor;
int fontTransp = lyr.textTransp;
QgsMapRenderer::BlendMode blendMode = lyr.blendMode;
double bufferSize = lyr.bufferSize;
QColor bufferColor = lyr.bufferColor;
int bufferTransp = lyr.bufferTransp;
QgsMapRenderer::BlendMode bufferBlendMode = lyr.bufferBlendMode;
// Copy to temp, editable layer settings
// these settings will be changed by any data defined values, then used for rendering label components
// settings may be adjusted during rendering of components
QgsPalLayerSettings tmpLyr( lyr );
//apply data defined settings for the label
//font size
QVariant dataDefinedSize = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Size );
if ( dataDefinedSize.isValid() )
{
fontForLabel.setPixelSize( lyr.sizeToPixel( dataDefinedSize.toDouble(), context ) );
tmpLyr.textFont.setPixelSize( lyr.sizeToPixel( dataDefinedSize.toDouble(), context,
( lyr.fontSizeInMapUnits ? QgsPalLayerSettings::MapUnits : QgsPalLayerSettings::Points ),
true ) );
}
//font color
QVariant dataDefinedColor = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Color );
if ( dataDefinedColor.isValid() )
{
fontColor.setNamedColor( dataDefinedColor.toString() );
if ( !fontColor.isValid() )
tmpLyr.textColor.setNamedColor( dataDefinedColor.toString() );
if ( !tmpLyr.textColor.isValid() )
{
fontColor = lyr.textColor;
tmpLyr.textColor = lyr.textColor;
}
}
//font transparency
@ -1684,56 +1794,56 @@ void QgsPalLabeling::drawLabeling( QgsRenderContext& context )
int ft = dataDefinedFontTransp.toInt( &ftOk );
if ( ftOk && ft >= 0 && ft <= 100 )
{
fontTransp = ft;
tmpLyr.textTransp = ft;
}
}
fontColor.setAlphaF(( 100.0 - ( double )( fontTransp ) ) / 100.0 );
tmpLyr.textColor.setAlphaF(( 100.0 - ( double )( tmpLyr.textTransp ) ) / 100.0 );
//font bold
QVariant dataDefinedBold = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Bold );
if ( dataDefinedBold.isValid() )
{
fontForLabel.setBold(( bool )dataDefinedBold.toInt() );
tmpLyr.textFont.setBold(( bool ) dataDefinedBold.toInt() );
}
//font italic
QVariant dataDefinedItalic = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Italic );
if ( dataDefinedItalic.isValid() )
{
fontForLabel.setItalic(( bool ) dataDefinedItalic.toInt() );
tmpLyr.textFont.setItalic(( bool ) dataDefinedItalic.toInt() );
}
//font underline
QVariant dataDefinedUnderline = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Underline );
if ( dataDefinedUnderline.isValid() )
{
fontForLabel.setUnderline(( bool ) dataDefinedUnderline.toInt() );
tmpLyr.textFont.setUnderline(( bool ) dataDefinedUnderline.toInt() );
}
//font strikeout
QVariant dataDefinedStrikeout = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Strikeout );
if ( dataDefinedStrikeout.isValid() )
{
fontForLabel.setStrikeOut(( bool ) dataDefinedStrikeout.toInt() );
tmpLyr.textFont.setStrikeOut(( bool ) dataDefinedStrikeout.toInt() );
}
//font family
QVariant dataDefinedFontFamily = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Family );
if ( dataDefinedFontFamily.isValid() )
{
fontForLabel.setFamily( dataDefinedFontFamily.toString() );
tmpLyr.textFont.setFamily( dataDefinedFontFamily.toString() );
}
//buffer size
QVariant dataDefinedBufferSize = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::BufferSize );
if ( dataDefinedBufferSize.isValid() )
{
bufferSize = dataDefinedBufferSize.toDouble();
tmpLyr.bufferSize = dataDefinedBufferSize.toDouble();
}
//buffer color
QVariant dataDefinedBufferColor = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::BufferColor );
if ( dataDefinedBufferColor.isValid() )
{
bufferColor.setNamedColor( dataDefinedBufferColor.toString() );
if ( !bufferColor.isValid() )
tmpLyr.bufferColor.setNamedColor( dataDefinedBufferColor.toString() );
if ( !tmpLyr.bufferColor.isValid() )
{
bufferColor = lyr.bufferColor;
tmpLyr.bufferColor = lyr.bufferColor;
}
}
//buffer transparency
@ -1744,24 +1854,25 @@ void QgsPalLabeling::drawLabeling( QgsRenderContext& context )
int bt = dataDefinedBufTransp.toInt( &btOk );
if ( btOk && bt >= 0 && bt <= 100 )
{
bufferTransp = bt;
tmpLyr.bufferTransp = bt;
}
}
bufferColor.setAlphaF(( 100.0 - ( double )( bufferTransp ) ) / 100.0 );
tmpLyr.bufferColor.setAlphaF(( 100.0 - ( double )( tmpLyr.bufferTransp ) ) / 100.0 );
if ( lyr.bufferSize != 0 )
// Render the components of a label in reverse order
// (backgrounds -> text)
if ( lyr.shapeDraw )
{
// Set the painter composition mode for the buffer
painter->setCompositionMode( mMapRenderer->getCompositionMode( bufferBlendMode ) );
int bufferPixelSize = lyr.sizeToPixel( bufferSize, context, true );
drawLabel( *it, painter, fontForLabel, fontColor, xform, bufferPixelSize, bufferColor, true );
drawLabel( *it, context, tmpLyr, LabelShape );
}
// Set the painter composition mode before rendering the label
painter->setCompositionMode( mMapRenderer->getCompositionMode( blendMode ) );
if ( lyr.bufferSize > 0 )
{
drawLabel( *it, context, tmpLyr, LabelBuffer );
}
drawLabel( *it, painter, fontForLabel, fontColor, xform );
drawLabel( *it, context, tmpLyr, LabelText );
if ( mLabelSearchTree )
{
@ -1889,152 +2000,400 @@ void QgsPalLabeling::drawLabelCandidateRect( pal::LabelPosition* lp, QPainter* p
drawLabelCandidateRect( lp->getNextPart(), painter, xform );
}
void QgsPalLabeling::drawLabel( pal::LabelPosition* label, QPainter* painter, const QFont& f, const QColor& c, const QgsMapToPixel* xform, double bufferPixelSize,
const QColor& bufferColor, bool drawBuffer )
void QgsPalLabeling::drawLabel( pal::LabelPosition* label, QgsRenderContext& context, QgsPalLayerSettings& tmpLyr, DrawLabelType drawType )
{
// NOTE: this is repeatedly called for multi-part labels
QPainter* painter = context.painter();
const QgsMapToPixel* xform = &context.mapToPixel();
QgsPoint outPt = xform->transform( label->getX(), label->getY() );
// TODO: optimize access :)
const QgsPalLayerSettings& lyr = layer( QString::fromUtf8( label->getLayerName() ) );
QString text = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->text();
QString txt = ( label->getPartId() == -1 ? text : QString( text[label->getPartId()] ) );
QFontMetricsF* labelfm = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->getLabelFontMetrics();
QString wrapchr = !lyr.wrapChar.isEmpty() ? lyr.wrapChar : QString( "\n" );
//add the direction symbol if needed
if ( !txt.isEmpty() && lyr.placement == QgsPalLayerSettings::Line &&
lyr.addDirectionSymbol )
if ( drawType == QgsPalLabeling::LabelShape )
{
bool prependSymb = false;
QString symb = lyr.rightDirectionSymbol;
// get rotated label's center point
QgsPoint centerPt( outPt );
QgsPoint outPt2 = xform->transform( label->getX() + label->getWidth() / 2,
label->getY() + label->getHeight() / 2 );
if ( label->getReversed() )
{
prependSymb = true;
symb = lyr.leftDirectionSymbol;
}
double xc = outPt2.x() - outPt.x();
double yc = outPt2.y() - outPt.y();
if ( lyr.reverseDirectionSymbol )
double angle = -label->getAlpha();
double xd = xc * cos( angle ) - yc * sin( angle );
double yd = xc * sin( angle ) + yc * cos( angle );
centerPt.setX( centerPt.x() + xd );
centerPt.setY( centerPt.y() + yd );
drawLabelBackground( context,
centerPt,
label->getAlpha() * 180 / M_PI, // converted to degrees
label->getWidth(), // in map units
label->getHeight(), // in map units
tmpLyr );
}
if ( drawType == QgsPalLabeling::LabelBuffer
|| drawType == QgsPalLabeling::LabelText )
{
// TODO: optimize access :)
QString text = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->text();
QString txt = ( label->getPartId() == -1 ? text : QString( text[label->getPartId()] ) );
QFontMetricsF* labelfm = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->getLabelFontMetrics();
QString wrapchr = !tmpLyr.wrapChar.isEmpty() ? tmpLyr.wrapChar : QString( "\n" );
//add the direction symbol if needed
if ( !txt.isEmpty() && tmpLyr.placement == QgsPalLayerSettings::Line &&
tmpLyr.addDirectionSymbol )
{
if ( symb == lyr.rightDirectionSymbol )
bool prependSymb = false;
QString symb = tmpLyr.rightDirectionSymbol;
if ( label->getReversed() )
{
prependSymb = true;
symb = lyr.leftDirectionSymbol;
symb = tmpLyr.leftDirectionSymbol;
}
if ( tmpLyr.reverseDirectionSymbol )
{
if ( symb == tmpLyr.rightDirectionSymbol )
{
prependSymb = true;
symb = tmpLyr.leftDirectionSymbol;
}
else
{
prependSymb = false;
symb = tmpLyr.rightDirectionSymbol;
}
}
if ( tmpLyr.placeDirectionSymbol == QgsPalLayerSettings::SymbolAbove )
{
prependSymb = true;
symb = symb + wrapchr;
}
else if ( tmpLyr.placeDirectionSymbol == QgsPalLayerSettings::SymbolBelow )
{
prependSymb = false;
symb = wrapchr + symb;
}
if ( prependSymb )
{
txt.prepend( symb );
}
else
{
prependSymb = false;
symb = lyr.rightDirectionSymbol;
txt.append( symb );
}
}
if ( lyr.placeDirectionSymbol == QgsPalLayerSettings::SymbolAbove )
{
prependSymb = true;
symb = symb + wrapchr;
}
else if ( lyr.placeDirectionSymbol == QgsPalLayerSettings::SymbolBelow )
{
prependSymb = false;
symb = wrapchr + symb;
}
//QgsDebugMsg( "drawLabel " + txt );
if ( prependSymb )
{
txt.prepend( symb );
}
else
{
txt.append( symb );
}
}
QStringList multiLineList = txt.split( wrapchr );
int lines = multiLineList.size();
//QgsDebugMsg( "drawLabel " + QString::number( drawBuffer ) + " " + txt );
QStringList multiLineList = txt.split( wrapchr );
int lines = multiLineList.size();
double labelWidest = 0.0;
for ( int i = 0; i < lines; ++i )
{
double labelWidth = labelfm->width( multiLineList.at( i ) );
if ( labelWidth > labelWidest )
{
labelWidest = labelWidth;
}
}
double labelHeight = labelfm->ascent() + labelfm->descent(); // ignore +1 for baseline
// needed to move bottom of text's descender to within bottom edge of label
double ascentOffset = 0.25 * labelfm->ascent(); // labelfm->descent() is not enough
for ( int i = 0; i < lines; ++i )
{
painter->save();
painter->translate( QPointF( outPt.x(), outPt.y() ) );
painter->rotate( -label->getAlpha() * 180 / M_PI );
// scale down painter: the font size has been multiplied by raster scale factor
// to workaround a Qt font scaling bug with small font sizes
painter->scale( 1.0 / lyr.rasterCompressFactor, 1.0 / lyr.rasterCompressFactor );
// figure x offset for horizontal alignment of multiple lines
double xMultiLineOffset = 0.0;
if ( lines > 1 && lyr.multilineAlign != QgsPalLayerSettings::MultiLeft )
double labelWidest = 0.0;
for ( int i = 0; i < lines; ++i )
{
double labelWidth = labelfm->width( multiLineList.at( i ) );
double labelWidthDiff = labelWidest - labelWidth;
if ( lyr.multilineAlign == QgsPalLayerSettings::MultiCenter )
if ( labelWidth > labelWidest )
{
labelWidthDiff /= 2;
labelWidest = labelWidth;
}
xMultiLineOffset = labelWidthDiff;
//QgsDebugMsg( QString( "xMultiLineOffset: %1" ).arg( xMultiLineOffset ) );
}
double yMultiLineOffset = ( lines - 1 - i ) * labelHeight * lyr.multilineHeight;
painter->translate( QPointF( xMultiLineOffset, - ascentOffset - yMultiLineOffset ) );
double labelHeight = labelfm->ascent() + labelfm->descent(); // ignore +1 for baseline
// double labelHighest = labelfm->height() + ( double )(( lines - 1 ) * labelHeight * tmpLyr.multilineHeight );
if ( drawBuffer )
// needed to move bottom of text's descender to within bottom edge of label
double ascentOffset = 0.25 * labelfm->ascent(); // labelfm->descent() is not enough
for ( int i = 0; i < lines; ++i )
{
// we're drawing buffer
//drawLabelBuffer( painter, multiLineList.at( i ), f, bufferSize * lyr.vectorScaleFactor * lyr.rasterCompressFactor , bufferColor );
drawLabelBuffer( painter, multiLineList.at( i ), f, bufferPixelSize , bufferColor, lyr.bufferJoinStyle, lyr.bufferNoFill );
painter->save();
painter->translate( QPointF( outPt.x(), outPt.y() ) );
painter->rotate( -label->getAlpha() * 180 / M_PI );
// scale down painter: the font size has been multiplied by raster scale factor
// to workaround a Qt font scaling bug with small font sizes
painter->scale( 1.0 / tmpLyr.rasterCompressFactor, 1.0 / tmpLyr.rasterCompressFactor );
// figure x offset for horizontal alignment of multiple lines
double xMultiLineOffset = 0.0;
if ( lines > 1 && tmpLyr.multilineAlign != QgsPalLayerSettings::MultiLeft )
{
double labelWidth = labelfm->width( multiLineList.at( i ) );
double labelWidthDiff = labelWidest - labelWidth;
if ( tmpLyr.multilineAlign == QgsPalLayerSettings::MultiCenter )
{
labelWidthDiff /= 2;
}
xMultiLineOffset = labelWidthDiff;
//QgsDebugMsg( QString( "xMultiLineOffset: %1" ).arg( xMultiLineOffset ) );
}
double yMultiLineOffset = ( lines - 1 - i ) * labelHeight * tmpLyr.multilineHeight;
painter->translate( QPointF( xMultiLineOffset, - ascentOffset - yMultiLineOffset ) );
if ( drawType == QgsPalLabeling::LabelBuffer )
{
// draw label's buffer
drawLabelBuffer( context, multiLineList.at( i ), tmpLyr );
}
else
{
// draw label's text
QPainterPath path;
path.addText( 0, 0, tmpLyr.textFont, multiLineList.at( i ) );
painter->setPen( Qt::NoPen );
painter->setBrush( tmpLyr.textColor );
painter->setCompositionMode( tmpLyr.blendMode );
painter->drawPath( path );
// regular text draw, for testing
// painter->setFont( tmpLyr.textFont );
// painter->setPen( tmpLyr.textColor );
// painter->setCompositionMode( tmpLyr.blendMode );
// painter->drawText( 0, 0, multiLineList.at( i ) );
}
painter->restore();
}
}
// NOTE: this used to be within above multi-line loop block, at end. (a mistake since 2010? [LS])
if ( label->getNextPart() )
drawLabel( label->getNextPart(), context, tmpLyr, drawType );
}
void QgsPalLabeling::drawLabelBuffer( QgsRenderContext& context, QString text, const QgsPalLayerSettings& tmpLyr )
{
QPainter* p = context.painter();
QPainterPath path;
path.addText( 0, 0, tmpLyr.textFont, text );
QPen pen( tmpLyr.bufferColor );
pen.setWidthF( tmpLyr.sizeToPixel( tmpLyr.bufferSize, context,
( tmpLyr.bufferSizeInMapUnits ? QgsPalLayerSettings::MapUnits : QgsPalLayerSettings::MM ),
true ) );
pen.setJoinStyle( tmpLyr.bufferJoinStyle );
p->setPen( pen );
QColor tmpColor( tmpLyr.bufferColor );
// honor pref for whether to fill buffer interior
if ( tmpLyr.bufferNoFill )
{
tmpColor.setAlpha( 0 );
}
p->setBrush( tmpColor );
p->setCompositionMode( tmpLyr.bufferBlendMode );
p->drawPath( path );
}
void QgsPalLabeling::drawLabelBackground( QgsRenderContext& context,
const QgsPoint& centerPt, double labelRotation, double labelWidth, double labelHeight,
const QgsPalLayerSettings& tmpLyr )
{
QPainter* p = context.painter();
// shared calculations between shapes and SVG
// aggregate angle
double angle = 0.0; // as degrees
if ( tmpLyr.shapeRotationType != QgsPalLayerSettings::RotationFixed )
{
angle = -labelRotation; // RotationSync
if ( tmpLyr.shapeRotationType == QgsPalLayerSettings::RotationOffset )
{
angle += tmpLyr.shapeRotation;
}
}
else
{
angle += tmpLyr.shapeRotation; // RotationFixed
}
// mm to map units conversion factor
double mmToMapUnits = context.mapToPixel().mapUnitsPerPixel() * context.scaleFactor();
// convert offsets to map pixels
double xoff = tmpLyr.sizeToPixel( tmpLyr.shapeOffset.x(), context, tmpLyr.shapeOffsetUnits );
double yoff = tmpLyr.sizeToPixel( tmpLyr.shapeOffset.y(), context, tmpLyr.shapeOffsetUnits );
// TODO: the following label-buffered generated shapes and SVG symbols should be moved into marker symbology classes
if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeSVG )
{
// all calculations done in shapeSizeUnits, which are then passed to symbology class for painting
if ( tmpLyr.shapeSVGFile.isEmpty() )
return;
double sizeOut = 0.0;
// only one size used for SVG sizing/scaling (no use of shapeSize.y() or Y field in gui)
if ( tmpLyr.shapeSizeType == QgsPalLayerSettings::SizeFixed )
{
sizeOut = tmpLyr.shapeSize.x();
}
else if ( tmpLyr.shapeSizeType == QgsPalLayerSettings::SizeBuffer )
{
// add buffer to greatest dimension of label
if ( labelWidth >= labelHeight )
sizeOut = labelWidth;
else if ( labelHeight > labelWidth )
sizeOut = labelHeight;
// label size in map units, convert to shapeSizeUnits, if different
if ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM )
{
sizeOut /= mmToMapUnits;
}
// add buffer
sizeOut += tmpLyr.shapeSize.x() * 2;
}
// don't bother rendering symbols smaller than 1x1 pixels in size
if ( tmpLyr.sizeToPixel( sizeOut, context, tmpLyr.shapeSizeUnits ) < 1 )
return;
QgsStringMap map; // for SVG symbology marker
map["name"] = tmpLyr.shapeSVGFile;
map["size"] = QString::number( sizeOut );
map["size_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit(
tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MapUnits ? QgsSymbolV2::MapUnit : QgsSymbolV2::MM );
map["angle"] = QString::number( angle );
// offset is added into QPointF passed to SVG renderer
// TODO: see why the marker renderer doesn't seem to translate offset *after* applying rotation
//map["offset"] = QgsSymbolLayerV2Utils::encodePoint( tmpLyr.shapeOffset );
//map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit(
// tmpLyr.shapeOffsetUnits == QgsPalLayerSettings::MapUnits ? QgsSymbolV2::MapUnit : QgsSymbolV2::MM );
map["fill"] = tmpLyr.shapeFillColor.name();
map["outline"] = tmpLyr.shapeBorderColor.name();
map["outline-width"] = QString::number( tmpLyr.shapeBorderWidth );
// TODO: fix overriding SVG symbol's border width units in QgsSvgCache
// currently broken, fall back to symbol's
//map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit(
// tmpLyr.shapeBorderWidthUnits == QgsPalLayerSettings::MapUnits ? QgsSymbolV2::MapUnit : QgsSymbolV2::MM );
QgsSymbolLayerV2* symL = QgsSvgMarkerSymbolLayerV2::create( map );
QgsSvgMarkerSymbolLayerV2* svgM = static_cast<QgsSvgMarkerSymbolLayerV2*>( symL );
QgsSymbolV2RenderContext svgContext( context, QgsSymbolV2::Mixed,
( 100.0 - ( double )( tmpLyr.shapeTransparency ) ) / 100.0 );
p->save();
p->setCompositionMode( tmpLyr.shapeBlendMode );
svgM->renderPoint( QPointF( centerPt.x() + xoff, centerPt.y() + yoff ), svgContext );
p->setCompositionMode( QPainter::CompositionMode_SourceOver ); // just to be sure
p->restore();
delete svgM;
svgM = 0;
}
else // Generated Shapes
{
// all calculations done in map units
double w = labelWidth;
double h = labelHeight;
double xsize = tmpLyr.shapeSize.x() * ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM ? mmToMapUnits : 1 );
double ysize = tmpLyr.shapeSize.y() * ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM ? mmToMapUnits : 1 );
if ( tmpLyr.shapeSizeType == QgsPalLayerSettings::SizeFixed )
{
w = xsize;
h = ysize;
}
else if ( tmpLyr.shapeSizeType == QgsPalLayerSettings::SizeBuffer )
{
if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeSquare )
{
if ( w > h )
h = w;
else if ( h > w )
w = h;
}
else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeCircle )
{
// start with label bound by circle
h = sqrt( pow( w, 2 ) + pow( h, 2 ) );
w = h;
}
else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeEllipse )
{
// start with label bound by ellipse
h = h / sqrt( 2.0 ) * 2;
w = w / sqrt( 2.0 ) * 2;
}
w += xsize * 2;
h += ysize * 2;
}
// convert everything over to map pixels from here on
// offsets match those of symbology: -x = left, -y = up
QRectF rect( -( w / 2 / context.mapToPixel().mapUnitsPerPixel() ),
-( h / 2 / context.mapToPixel().mapUnitsPerPixel() ),
( w / context.mapToPixel().mapUnitsPerPixel() ),
( h / context.mapToPixel().mapUnitsPerPixel() ) );
if ( rect.isNull() )
return;
p->save();
p->translate( QPointF( centerPt.x() + xoff, centerPt.y() + yoff ) );
p->rotate( angle );
if ( tmpLyr.shapeBorderWidth > 0 )
{
QPen pen( tmpLyr.shapeBorderColor );
pen.setWidthF( tmpLyr.sizeToPixel( tmpLyr.shapeBorderWidth, context, tmpLyr.shapeBorderWidthUnits ) );
if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeRectangle )
pen.setJoinStyle( tmpLyr.shapeJoinStyle );
p->setPen( pen );
}
else
{
// we're drawing real label
QPainterPath path;
path.addText( 0, 0, f, multiLineList.at( i ) );
painter->setPen( Qt::NoPen );
painter->setBrush( c );
painter->drawPath( path );
p->setPen( Qt::NoPen );
}
painter->restore();
p->setBrush( tmpLyr.shapeFillColor );
if ( label->getNextPart() )
drawLabel( label->getNextPart(), painter, f, c, xform, bufferPixelSize, bufferColor, drawBuffer );
p->setOpacity(( 100.0 - ( double )( tmpLyr.shapeTransparency ) ) / 100.0 );
p->setCompositionMode( tmpLyr.shapeBlendMode );
if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeRectangle
|| tmpLyr.shapeType == QgsPalLayerSettings::ShapeSquare )
{
if ( tmpLyr.shapeRadiiUnits == QgsPalLayerSettings::Percent )
{
p->drawRoundedRect( rect, tmpLyr.shapeRadii.x(), tmpLyr.shapeRadii.y(), Qt::RelativeSize );
}
else
{
double xRadius = tmpLyr.sizeToPixel( tmpLyr.shapeRadii.x(), context, tmpLyr.shapeRadiiUnits );
double yRadius = tmpLyr.sizeToPixel( tmpLyr.shapeRadii.y(), context, tmpLyr.shapeRadiiUnits );
p->drawRoundedRect( rect, xRadius, yRadius );
}
}
else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeEllipse
|| tmpLyr.shapeType == QgsPalLayerSettings::ShapeCircle )
{
p->drawEllipse( rect );
}
p->restore();
}
}
void QgsPalLabeling::drawLabelBuffer( QPainter* p, QString text, const QFont& font, double size, QColor color, Qt::PenJoinStyle joinstyle, bool noFill )
{
QPainterPath path;
path.addText( 0, 0, font, text );
QPen pen( color );
pen.setWidthF( size );
pen.setJoinStyle( joinstyle );
p->setPen( pen );
// honor pref for whether to fill buffer
if ( noFill )
{
color.setAlpha( 0 );
}
p->setBrush( color );
p->drawPath( path );
}
void QgsPalLabeling::loadEngineSettings()

View File

@ -102,6 +102,38 @@ class CORE_EXPORT QgsPalLayerSettings
MultiRight
};
enum ShapeType
{
ShapeRectangle = 0,
ShapeSquare,
ShapeEllipse,
ShapeCircle,
ShapeSVG
};
enum SizeType
{
SizeBuffer = 0,
SizeFixed,
SizePercent
};
enum RotationType
{
RotationSync = 0,
RotationOffset,
RotationFixed
};
/** Units used for option sizes, before being converted to rendered sizes */
enum SizeUnit
{
Points = 0,
MM,
MapUnits,
Percent
};
// update mDataDefinedNames QList in constructor when adding/deleting enum value
enum DataDefinedProperties
{
@ -154,7 +186,7 @@ class CORE_EXPORT QgsPalLayerSettings
QString textNamedStyle;
QColor textColor;
int textTransp;
QgsMapRenderer::BlendMode blendMode;
QPainter::CompositionMode blendMode;
QColor previewBkgrdColor;
bool enabled;
int priority; // 0 = low, 10 = high
@ -166,12 +198,34 @@ class CORE_EXPORT QgsPalLayerSettings
// disabled if both are zero
int scaleMin;
int scaleMax;
double bufferSize; //buffer size (in mm)
double bufferSize; //buffer size
QColor bufferColor;
int bufferTransp;
QgsMapRenderer::BlendMode bufferBlendMode;
QPainter::CompositionMode bufferBlendMode;
Qt::PenJoinStyle bufferJoinStyle;
bool bufferNoFill; //set interior of buffer to 100% transparent
// shape background
bool shapeDraw;
ShapeType shapeType;
QString shapeSVGFile;
SizeType shapeSizeType;
QPointF shapeSize;
SizeUnit shapeSizeUnits;
RotationType shapeRotationType;
double shapeRotation;
QPointF shapeOffset;
SizeUnit shapeOffsetUnits;
QPointF shapeRadii;
SizeUnit shapeRadiiUnits;
QColor shapeFillColor;
QColor shapeBorderColor;
double shapeBorderWidth;
SizeUnit shapeBorderWidthUnits;
Qt::PenJoinStyle shapeJoinStyle;
int shapeTransparency;
QPainter::CompositionMode shapeBlendMode;
bool formatNumbers;
int decimals;
bool plusSign;
@ -222,12 +276,14 @@ class CORE_EXPORT QgsPalLayerSettings
bool preserveRotation; // preserve predefined rotation data during label pin/unpin operations
/**Calculates pixel size (considering output size should be in pixel or map units, scale factors and oversampling)
@param size size to convert
@param c rendercontext
@param buffer whether it buffer size being calculated
@return font pixel size*/
int sizeToPixel( double size, const QgsRenderContext& c , bool buffer = false ) const;
/** Calculates pixel size (considering output size should be in pixel or map units, scale factors and optionally oversampling)
* @param size size to convert
* @param c rendercontext
* @param unit SizeUnit enum value of size
* @param rasterfactor whether to consider oversampling
* @return font pixel size
*/
int sizeToPixel( double size, const QgsRenderContext& c , SizeUnit unit, bool rasterfactor = false ) const;
/** List of data defined enum names
* @note adding in 1.9
@ -282,6 +338,15 @@ class CORE_EXPORT QgsLabelCandidate
class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
{
public:
enum DrawLabelType
{
LabelText = 0,
LabelBuffer,
LabelShape,
LabelSVG,
LabelShadow
};
QgsPalLabeling();
~QgsPalLabeling();
@ -331,9 +396,13 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
void drawLabelCandidateRect( pal::LabelPosition* lp, QPainter* painter, const QgsMapToPixel* xform );
//!drawLabel
//! @note not available in python bindings
void drawLabel( pal::LabelPosition* label, QPainter* painter, const QFont& f, const QColor& c, const QgsMapToPixel* xform, double bufferSize = -1,
const QColor& bufferColor = QColor( 255, 255, 255 ), bool drawBuffer = false );
static void drawLabelBuffer( QPainter* p, QString text, const QFont& font, double size, QColor color , Qt::PenJoinStyle joinstyle = Qt::BevelJoin, bool noFill = false );
void drawLabel( pal::LabelPosition* label, QgsRenderContext& context, QgsPalLayerSettings& tmpLyr, DrawLabelType drawType );
static void drawLabelBuffer( QgsRenderContext& context, QString text, const QgsPalLayerSettings& tmpLyr );
static void drawLabelBackground( QgsRenderContext& context,
const QgsPoint& centerPt, double labelRotation, double labelWidth, double labelHeight,
const QgsPalLayerSettings& tmpLyr );
//! load/save engine settings to project file
//! @note added in QGIS 1.9

View File

@ -998,7 +998,7 @@ QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::create( const QgsStringMap& props )
}
if ( props.contains( "size_unit" ) )
m->setSizeUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( "size_unit" ) );
m->setSizeUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["size_unit"] ) );
if ( props.contains( "offset" ) )
m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) );
if ( props.contains( "offset_unit" ) )

View File

@ -34,6 +34,7 @@ symbology-ng/qgsellipsesymbollayerv2widget.cpp
symbology-ng/qgspointdisplacementrendererwidget.cpp
symbology-ng/qgsvectorfieldsymbollayerwidget.cpp
symbology-ng/qgssymbolslistwidget.cpp
symbology-ng/qgssvgselectorwidget.cpp
symbology-ng/qgslayerpropertieswidget.cpp
symbology-ng/qgssmartgroupeditordialog.cpp
@ -158,6 +159,7 @@ symbology-ng/qgsellipsesymbollayerv2widget.h
symbology-ng/qgspointdisplacementrendererwidget.h
symbology-ng/qgsvectorfieldsymbollayerwidget.h
symbology-ng/qgssymbolslistwidget.h
symbology-ng/qgssvgselectorwidget.h
symbology-ng/qgslayerpropertieswidget.h
symbology-ng/qgssmartgroupeditordialog.h

View File

@ -105,6 +105,17 @@ class GUI_EXPORT QgsColorButton: public QPushButton
*/
void setAcceptLiveUpdates( bool accept ) { mAcceptLiveUpdates = accept; }
public slots:
/**
* Sets the background pixmap for the button based upon set color and transparency.
* Call directly to update background after adding/removing QColorDialog::ShowAlphaChannel option
* but the color has not changed, i.e. setColor() wouldn't update button and
* you want the button to retain the set color's alpha component regardless
*
* @note added in 1.9
*/
void setButtonBackground();
signals:
/**
* Is emitted, whenever a new color is accepted. The color is always valid.
@ -133,13 +144,6 @@ class GUI_EXPORT QgsColorButton: public QPushButton
private slots:
void onButtonClicked();
/**
* Sets the background pixmap for the button based upon set color and transparency.
*
* @note added in 1.9
*/
void setButtonBackground();
/**
* Sets color for button, if valid.
*

View File

@ -0,0 +1,291 @@
/***************************************************************************
qgssvgselectorwidget.cpp - group and preview selector for SVG files
built off of work in qgssymbollayerv2widget
---------------------
begin : April 2, 2013
copyright : (C) 2013 by Larry Shaffer
email : larrys at dakcarto dot com
***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#include "qgssvgselectorwidget.h"
#include "qgsapplication.h"
#include "qgslogger.h"
#include "qgssvgcache.h"
#include "qgssymbollayerv2utils.h"
#include <QAbstractListModel>
#include <QDir>
#include <QFileDialog>
#include <QMessageBox>
#include <QModelIndex>
#include <QPixmapCache>
#include <QSettings>
#include <QStyle>
#include <QTime>
//--- QgsSvgSelectorListModel
QgsSvgSelectorListModel::QgsSvgSelectorListModel( QObject* parent )
: QAbstractListModel( parent )
{
mSvgFiles = QgsSymbolLayerV2Utils::listSvgFiles();
}
// Constructor to create model for icons in a specific path
QgsSvgSelectorListModel::QgsSvgSelectorListModel( QObject* parent, QString path )
: QAbstractListModel( parent )
{
mSvgFiles = QgsSymbolLayerV2Utils::listSvgFilesAt( path );
}
int QgsSvgSelectorListModel::rowCount( const QModelIndex & parent ) const
{
Q_UNUSED( parent );
return mSvgFiles.count();
}
QVariant QgsSvgSelectorListModel::data( const QModelIndex & index, int role ) const
{
QString entry = mSvgFiles.at( index.row() );
if ( role == Qt::DecorationRole ) // icon
{
QPixmap pixmap;
if ( !QPixmapCache::find( entry, pixmap ) )
{
// render SVG file
QColor fill, outline;
double outlineWidth;
bool fillParam, outlineParam, outlineWidthParam;
QgsSvgCache::instance()->containsParams( entry, fillParam, fill, outlineParam, outline, outlineWidthParam, outlineWidth );
bool fitsInCache; // should always fit in cache at these sizes (i.e. under 559 px ^ 2, or half cache size)
const QImage& img = QgsSvgCache::instance()->svgAsImage( entry, 30.0, fill, outline, outlineWidth, 3.5 /*appr. 88 dpi*/, 1.0, fitsInCache );
pixmap = QPixmap::fromImage( img );
QPixmapCache::insert( entry, pixmap );
}
return pixmap;
}
else if ( role == Qt::UserRole || role == Qt::ToolTipRole )
{
return entry;
}
return QVariant();
}
//--- QgsSvgSelectorGroupsModel
QgsSvgSelectorGroupsModel::QgsSvgSelectorGroupsModel( QObject* parent )
: QStandardItemModel( parent )
{
QStringList svgPaths = QgsApplication::svgPaths();
QStandardItem *parentItem = invisibleRootItem();
for ( int i = 0; i < svgPaths.size(); i++ )
{
QDir dir( svgPaths[i] );
QStandardItem *baseGroup;
if ( dir.path().contains( QgsApplication::pkgDataPath() ) )
{
baseGroup = new QStandardItem( tr( "App Symbols" ) );
}
else if ( dir.path().contains( QgsApplication::qgisSettingsDirPath() ) )
{
baseGroup = new QStandardItem( tr( "User Symbols" ) );
}
else
{
baseGroup = new QStandardItem( dir.dirName() );
}
baseGroup->setData( QVariant( svgPaths[i] ) );
baseGroup->setEditable( false );
baseGroup->setCheckable( false );
baseGroup->setIcon( QgsApplication::style()->standardIcon( QStyle::SP_DirIcon ) );
baseGroup->setToolTip( dir.path() );
parentItem->appendRow( baseGroup );
createTree( baseGroup );
QgsDebugMsg( QString( "SVG base path %1: %2" ).arg( i ).arg( baseGroup->data().toString() ) );
}
}
void QgsSvgSelectorGroupsModel::createTree( QStandardItem* &parentGroup )
{
QDir parentDir( parentGroup->data().toString() );
foreach ( QString item, parentDir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
{
QStandardItem* group = new QStandardItem( item );
group->setData( QVariant( parentDir.path() + "/" + item ) );
group->setEditable( false );
group->setCheckable( false );
group->setToolTip( parentDir.path() + "/" + item );
group->setIcon( QgsApplication::style()->standardIcon( QStyle::SP_DirIcon ) );
parentGroup->appendRow( group );
createTree( group );
}
}
//-- QgsSvgSelectorWidget
QgsSvgSelectorWidget::QgsSvgSelectorWidget( QWidget* parent )
: QWidget( parent )
{
setupUi( this );
mGroupsTreeView->setHeaderHidden( true );
populateList();
connect( mImagesListView->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ),
this, SLOT( svgSelectionChanged( const QModelIndex& ) ) );
connect( mGroupsTreeView->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ),
this, SLOT( populateIcons( const QModelIndex& ) ) );
connect( this, SIGNAL( svgSelected( const QString& ) ), this, SLOT( updateCurrentSvgPath( const QString& ) ) );
}
QgsSvgSelectorWidget::~QgsSvgSelectorWidget()
{
}
void QgsSvgSelectorWidget::setSvgPath( const QString& svgPath )
{
mCurrentSvgPath = svgPath;
mFileLineEdit->blockSignals( true );
mFileLineEdit->setText( svgPath );
mFileLineEdit->blockSignals( false );
mImagesListView->selectionModel()->blockSignals( true );
QAbstractItemModel* m = mImagesListView->model();
QItemSelectionModel* selModel = mImagesListView->selectionModel();
for ( int i = 0; i < m->rowCount(); i++ )
{
QModelIndex idx( m->index( i, 0 ) );
if ( m->data( idx ).toString() == svgPath )
{
selModel->select( idx, QItemSelectionModel::SelectCurrent );
selModel->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent );
mImagesListView->scrollTo( idx );
break;
}
}
mImagesListView->selectionModel()->blockSignals( false );
}
QString QgsSvgSelectorWidget::currentSvgPath() const
{
return mCurrentSvgPath;
}
void QgsSvgSelectorWidget::updateCurrentSvgPath( const QString& svgPath )
{
mCurrentSvgPath = svgPath;
}
void QgsSvgSelectorWidget::svgSelectionChanged( const QModelIndex& idx )
{
QString filePath = idx.data( Qt::UserRole ).toString();
mFileLineEdit->setText( filePath );
emit svgSelected( filePath );
}
void QgsSvgSelectorWidget::populateIcons( const QModelIndex& idx )
{
QString path = idx.data( Qt::UserRole + 1 ).toString();
QgsSvgSelectorListModel* m = new QgsSvgSelectorListModel( mImagesListView, path );
mImagesListView->setModel( m );
connect( mImagesListView->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ),
this, SLOT( svgSelectionChanged( const QModelIndex& ) ) );
}
void QgsSvgSelectorWidget::on_mFilePushButton_clicked()
{
QSettings settings;
QString openDir = settings.value( "/UI/lastSVGMarkerDir", "." ).toString();
QString lineEditText = mFileLineEdit->text();
if ( !lineEditText.isEmpty() )
{
QFileInfo openDirFileInfo( lineEditText );
openDir = openDirFileInfo.path();
}
QString file = QFileDialog::getOpenFileName( 0,
tr( "Select SVG file" ),
openDir,
tr( "SVG files" ) + " (*.svg *.SVG)" );
activateWindow(); // return window focus
if ( file.isNull() )
return;
QFileInfo fi( file );
if ( !fi.exists() || !fi.isReadable() )
{
emit svgSelected( QString( "" ) );
QMessageBox::critical( 0, tr( "Invalid file" ), tr( "Error, file does not exist or is not readable" ) );
return;
}
settings.setValue( "/UI/lastSVGMarkerDir", fi.absolutePath() );
mFileLineEdit->setText( file );
emit svgSelected( file );
}
void QgsSvgSelectorWidget::on_mFileLineEdit_textEdited( const QString& text )
{
if ( !QFileInfo( text ).exists() )
{
emit svgSelected( QString( "" ) );
return;
}
emit svgSelected( text );
}
void QgsSvgSelectorWidget::on_mFileLineEdit_editingFinished()
{
if ( !QFileInfo( mFileLineEdit->text() ).exists() )
{
QUrl url( mFileLineEdit->text() );
if ( !url.isValid() )
{
emit svgSelected( QString( "" ) );
QMessageBox::critical( 0, tr( "Invalid file url" ), tr( "Error, file URL is invalid" ) );
return;
}
emit svgSelected( QString( "" ) );
QMessageBox::critical( 0, tr( "Invalid file" ), tr( "Error, file does not exist or is not readable" ) );
return;
}
emit svgSelected( mFileLineEdit->text() );
}
void QgsSvgSelectorWidget::populateList()
{
QgsSvgSelectorGroupsModel* g = new QgsSvgSelectorGroupsModel( mGroupsTreeView );
mGroupsTreeView->setModel( g );
// Set the tree expanded at the first level
int rows = g->rowCount( g->indexFromItem( g->invisibleRootItem() ) );
for ( int i = 0; i < rows; i++ )
{
mGroupsTreeView->setExpanded( g->indexFromItem( g->item( i ) ), true );
}
// Initally load the icons in the List view without any grouping
QgsSvgSelectorListModel* m = new QgsSvgSelectorListModel( mImagesListView );
mImagesListView->setModel( m );
}

View File

@ -0,0 +1,99 @@
/***************************************************************************
qgssvgselectorwidget.h - group and preview selector for SVG files
built off of work in qgssymbollayerv2widget
---------------------
begin : April 2, 2013
copyright : (C) 2013 by Larry Shaffer
email : larrys at dakcarto dot com
***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef QGSSVGSELECTORWIDGET_H
#define QGSSVGSELECTORWIDGET_H
#include "ui_widget_svgselector.h"
#include <QAbstractListModel>
#include <QStandardItemModel>
#include <QWidget>
class QLayout;
class QLineEdit;
class QListView;
class QPushButton;
class QTreeView;
class GUI_EXPORT QgsSvgSelectorListModel : public QAbstractListModel
{
public:
QgsSvgSelectorListModel( QObject* parent );
// Constructor to create model for icons in a specific path
QgsSvgSelectorListModel( QObject* parent, QString path );
int rowCount( const QModelIndex & parent = QModelIndex() ) const;
QVariant data( const QModelIndex & index, int role = Qt::DisplayRole ) const;
protected:
QStringList mSvgFiles;
};
class GUI_EXPORT QgsSvgSelectorGroupsModel : public QStandardItemModel
{
public:
QgsSvgSelectorGroupsModel( QObject* parent );
private:
void createTree( QStandardItem* &parentGroup );
};
class GUI_EXPORT QgsSvgSelectorWidget : public QWidget, private Ui::WidgetSvgSelector
{
Q_OBJECT
public:
QgsSvgSelectorWidget( QWidget* parent = 0 );
~QgsSvgSelectorWidget();
static QgsSvgSelectorWidget* create( QWidget* parent = 0 ) { return new QgsSvgSelectorWidget( parent ); }
QString currentSvgPath() const;
QTreeView* groupsTreeView() { return mGroupsTreeView; }
QListView* imagesListView() { return mImagesListView; }
QLineEdit* filePathLineEdit() { return mFileLineEdit; }
QPushButton* filePathButton() { return mFilePushButton; }
QLayout* selectorLayout() { return this->layout(); }
public slots:
void setSvgPath( const QString& svgPath );
signals:
void svgSelected( const QString& path );
protected:
void populateList();
private slots:
void populateIcons( const QModelIndex& idx );
void svgSelectionChanged( const QModelIndex& idx );
void updateCurrentSvgPath( const QString& svgPath );
void on_mFilePushButton_clicked();
void on_mFileLineEdit_textEdited( const QString& text );
void on_mFileLineEdit_editingFinished();
private:
QString mCurrentSvgPath;
};
#endif // QGSSVGSELECTORWIDGET_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WidgetSvgSelector</class>
<widget class="QWidget" name="WidgetSvgSelector">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>405</width>
<height>490</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="margin">
<number>0</number>
</property>
<item row="1" column="1">
<widget class="QLabel" name="mImagesLabel">
<property name="text">
<string>SVG Images</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="mGroupsLabel">
<property name="text">
<string>SVG Groups</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QTreeView" name="mGroupsTreeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>3</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QListView" name="mImagesListView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>5</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="movement">
<enum>QListView::Static</enum>
</property>
<property name="resizeMode">
<enum>QListView::Adjust</enum>
</property>
<property name="layoutMode">
<enum>QListView::Batched</enum>
</property>
<property name="spacing">
<number>2</number>
</property>
<property name="gridSize">
<size>
<width>36</width>
<height>36</height>
</size>
</property>
<property name="viewMode">
<enum>QListView::IconMode</enum>
</property>
<property name="uniformItemSizes">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<layout class="QHBoxLayout" name="mFileLayout">
<item>
<widget class="QLineEdit" name="mFileLineEdit"/>
</item>
<item>
<widget class="QPushButton" name="mFilePushButton">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<tabstops>
<tabstop>mImagesListView</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>