Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE] Substitution list support for labeling
Adds the ability to specify a list of text substitutes to make
which apply to label text. Eg abbrevating street types.

Users can export and import lists of substitutes to make
reuse and sharing easier.
  • Loading branch information
nyalldawson committed Aug 29, 2016
1 parent d477d19 commit 46fba7c
Show file tree
Hide file tree
Showing 17 changed files with 1,360 additions and 265 deletions.
5 changes: 5 additions & 0 deletions python/core/qgspallabeling.sip
Expand Up @@ -464,6 +464,11 @@ class QgsPalLayerSettings
QPainter::CompositionMode blendMode;
QColor previewBkgrdColor;

//! Substitution collection for automatic text substitution with labels
QgsStringReplacementCollection substitutions;
//! True if substitutions should be applied
bool useSubstitutions;

//-- text formatting

QString wrapChar;
Expand Down
112 changes: 112 additions & 0 deletions python/core/qgsstringutils.sip
@@ -1,3 +1,115 @@


/** \ingroup core
* \class QgsStringReplacement
* \brief A representation of a single string replacement.
* \note Added in version 3.0
*/

class QgsStringReplacement
{
%TypeHeaderCode
#include <qgsstringutils.h>
%End
public:

/** Constructor for QgsStringReplacement.
* @param match string to match
* @param replacement string to replace match with
* @param caseSensitive set to true for a case sensitive match
* @param wholeWordOnly set to true to match complete words only, or false to allow partial word matches
*/
QgsStringReplacement( const QString& match,
const QString& replacement,
bool caseSensitive = false,
bool wholeWordOnly = false );

//! Returns the string matched by this object
QString match() const;

//! Returns the string to replace matches with
QString replacement() const;

//! Returns true if match is case sensitive
bool caseSensitive() const;

//! Returns true if match only applies to whole words, or false if partial word matches are permitted
bool wholeWordOnly() const;

/** Processes a given input string, applying any valid replacements which should be made.
* @param input input string
* @returns input string with any matches replaced by replacement string
*/
QString process( const QString& input ) const;

bool operator==( const QgsStringReplacement& other );

/** Returns a map of the replacement properties.
* @see fromProperties()
*/
QgsStringMap properties() const;

/** Creates a new QgsStringReplacement from an encoded properties map.
* @see properties()
*/
static QgsStringReplacement fromProperties( const QgsStringMap& properties );

};


/** \ingroup core
* \class QgsStringReplacementCollection
* \brief A collection of string replacements (specified using QgsStringReplacement objects).
* \note Added in version 3.0
*/

class QgsStringReplacementCollection
{
%TypeHeaderCode
#include <qgsstringutils.h>
%End
public:

/** Constructor for QgsStringReplacementCollection
* @param replacements initial list of string replacements
*/
QgsStringReplacementCollection( const QList< QgsStringReplacement >& replacements = QList< QgsStringReplacement >() );

/** Returns the list of string replacements in this collection.
* @see setReplacements()
*/
QList< QgsStringReplacement > replacements() const;

/** Sets the list of string replacements in this collection.
* @param replacements list of string replacements to apply. Replacements are applied in the
* order they are specified here.
* @see replacements()
*/
void setReplacements( const QList< QgsStringReplacement >& replacements );

/** Processes a given input string, applying any valid replacements which should be made
* using QgsStringReplacement objects contained by this collection. Replacements
* are made in order of the QgsStringReplacement objects contained in the collection.
* @param input input string
* @returns input string with any matches replaced by replacement string
*/
QString process( const QString& input ) const;

/** Writes the collection state to an XML element.
* @param elem target DOM element
* @param doc DOM document
* @see readXml()
*/
void writeXml( QDomElement& elem, QDomDocument& doc ) const;

/** Reads the collection state from an XML element.
* @param elem DOM element
* @see writeXml()
*/
void readXml( const QDomElement& elem );
};


/** \ingroup core
* \class QgsStringUtils
* \brief Utility functions for working with strings.
Expand Down
2 changes: 2 additions & 0 deletions src/app/CMakeLists.txt
Expand Up @@ -114,6 +114,7 @@ SET(QGIS_APP_SRCS
qgsrelationadddlg.cpp
qgsselectbyformdialog.cpp
qgsstatisticalsummarydockwidget.cpp
qgssubstitutionlistwidget.cpp
qgstextannotationdialog.cpp
qgssnappingdialog.cpp
qgssvgannotationdialog.cpp
Expand Down Expand Up @@ -290,6 +291,7 @@ SET (QGIS_APP_MOC_HDRS
qgssnappingdialog.h
qgssponsors.h
qgsstatisticalsummarydockwidget.h
qgssubstitutionlistwidget.h
qgssvgannotationdialog.h
qgstextannotationdialog.h
qgstipgui.h
Expand Down
37 changes: 36 additions & 1 deletion src/app/qgslabelinggui.cpp
Expand Up @@ -34,6 +34,7 @@
#include "qgssvgselectorwidget.h"
#include "qgsvectorlayerlabeling.h"
#include "qgslogger.h"
#include "qgssubstitutionlistwidget.h"

#include <QCheckBox>
#include <QSettings>
Expand Down Expand Up @@ -137,6 +138,7 @@ QgsLabelingGui::QgsLabelingGui( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas,
connect( mShadowTranspSlider, SIGNAL( valueChanged( int ) ), mShadowTranspSpnBx, SLOT( setValue( int ) ) );
connect( mShadowTranspSpnBx, SIGNAL( valueChanged( int ) ), mShadowTranspSlider, SLOT( setValue( int ) ) );
connect( mLimitLabelChkBox, SIGNAL( toggled( bool ) ), mLimitLabelSpinBox, SLOT( setEnabled( bool ) ) );
connect( mCheckBoxSubstituteText, SIGNAL( toggled( bool ) ), mToolButtonConfigureSubstitutes, SLOT( setEnabled( bool ) ) );

//connections to prevent users removing all line placement positions
connect( chkLineAbove, SIGNAL( toggled( bool ) ), this, SLOT( updateLinePlacementOptions() ) );
Expand Down Expand Up @@ -466,7 +468,8 @@ QgsLabelingGui::QgsLabelingGui( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas,
<< radPolygonPerimeter
<< radPolygonPerimeterCurved
<< radPredefinedOrder
<< mFieldExpressionWidget;
<< mFieldExpressionWidget
<< mCheckBoxSubstituteText;
connectValueChanged( widgets, SLOT( updatePreview() ) );

connect( mQuadrantBtnGrp, SIGNAL( buttonClicked( int ) ), this, SLOT( updatePreview() ) );
Expand Down Expand Up @@ -623,6 +626,8 @@ void QgsLabelingGui::init()
// set the current field or add the current expression to the bottom of the list
mFieldExpressionWidget->setRow( -1 );
mFieldExpressionWidget->setField( lyr.fieldName );
mCheckBoxSubstituteText->setChecked( lyr.useSubstitutions );
mSubstitutions = lyr.substitutions;

// populate placement options
mCentroidRadioWhole->setChecked( lyr.centroidWhole );
Expand Down Expand Up @@ -1013,6 +1018,8 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
lyr.scaleVisibility = mScaleBasedVisibilityChkBx->isChecked();
lyr.scaleMin = mScaleBasedVisibilityMinSpnBx->value();
lyr.scaleMax = mScaleBasedVisibilityMaxSpnBx->value();
lyr.useSubstitutions = mCheckBoxSubstituteText->isChecked();
lyr.substitutions = mSubstitutions;

// buffer
lyr.bufferDraw = mBufferDrawChkBx->isChecked();
Expand Down Expand Up @@ -1973,6 +1980,12 @@ void QgsLabelingGui::updateLinePlacementOptions()
}
}

void QgsLabelingGui::onSubstitutionsChanged( const QgsStringReplacementCollection& substitutions )
{
mSubstitutions = substitutions;
emit widgetChanged();
}

void QgsLabelingGui::updateSvgWidgets( const QString& svgPath )
{
if ( mShapeSVGPathLineEdit->text() != svgPath )
Expand Down Expand Up @@ -2099,6 +2112,28 @@ void QgsLabelingGui::on_mChkNoObstacle_toggled( bool active )
mObstaclePriorityFrame->setEnabled( active );
}

void QgsLabelingGui::on_mToolButtonConfigureSubstitutes_clicked()
{
QgsPanelWidget* panel = QgsPanelWidget::findParentPanel( this );
if ( panel && panel->dockMode() )
{
QgsSubstitutionListWidget* widget = new QgsSubstitutionListWidget( panel );
widget->setPanelTitle( tr( "Substitutions" ) );
widget->setSubstitutions( mSubstitutions );
connect( widget, SIGNAL( substitutionsChanged( QgsStringReplacementCollection ) ), this, SLOT( onSubstitutionsChanged( QgsStringReplacementCollection ) ) );
panel->openPanel( widget );
return;
}

QgsSubstitutionListDialog dlg( this );
dlg.setSubstitutions( mSubstitutions );
if ( dlg.exec() == QDialog::Accepted )
{
mSubstitutions = dlg.substitutions();
emit widgetChanged();
}
}

void QgsLabelingGui::showBackgroundRadius( bool show )
{
mShapeRadiusLabel->setVisible( show );
Expand Down
6 changes: 6 additions & 0 deletions src/app/qgslabelinggui.h
Expand Up @@ -21,6 +21,7 @@
#include <QDialog>
#include <QFontDatabase>
#include <ui_qgslabelingguibase.h>
#include "qgsstringutils.h"

class QgsVectorLayer;
class QgsMapCanvas;
Expand Down Expand Up @@ -94,6 +95,8 @@ class APP_EXPORT QgsLabelingGui : public QWidget, private Ui::QgsLabelingGuiBase
void on_mDirectSymbRightToolBtn_clicked();
void on_mChkNoObstacle_toggled( bool active );

void on_mToolButtonConfigureSubstitutes_clicked();

protected:
void blockInitSignals( bool block );
void blockFontChangeSignals( bool blk );
Expand Down Expand Up @@ -133,6 +136,8 @@ class APP_EXPORT QgsLabelingGui : public QWidget, private Ui::QgsLabelingGuiBase

bool mLoadSvgParams;

QgsStringReplacementCollection mSubstitutions;

void enableDataDefinedAlignment( bool enable );

QgsExpressionContext createExpressionContext() const override;
Expand All @@ -143,6 +148,7 @@ class APP_EXPORT QgsLabelingGui : public QWidget, private Ui::QgsLabelingGuiBase
void showBackgroundPenStyle( bool show );
void on_mShapeSVGPathLineEdit_textChanged( const QString& text );
void updateLinePlacementOptions();
void onSubstitutionsChanged( const QgsStringReplacementCollection& substitutions );
};

#endif
Expand Down

0 comments on commit 46fba7c

Please sign in to comment.