Skip to content

Commit 847204e

Browse files
committed
Merge branch 'master', remote-tracking branch 'nathan/expression-labels'
2 parents 3a15509 + 665e92e commit 847204e

15 files changed

+1497
-122
lines changed

src/app/qgslabelinggui.cpp

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,21 @@
2424

2525
#include "qgspallabeling.h"
2626
#include "qgslabelengineconfigdialog.h"
27+
#include "qgsexpressionbuilderdialog.h"
28+
#include "qgsexpression.h"
2729

2830
#include <QColorDialog>
2931
#include <QFontDialog>
30-
32+
#include <QTextEdit>
3133
#include <iostream>
3234
#include <QApplication>
33-
34-
35+
#include <QMessageBox>
3536

3637
QgsLabelingGui::QgsLabelingGui( QgsPalLabeling* lbl, QgsVectorLayer* layer, QgsMapCanvas* mapCanvas, QWidget* parent )
3738
: QDialog( parent ), mLBL( lbl ), mLayer( layer ), mMapCanvas( mapCanvas )
3839
{
40+
if ( !layer ) return;
41+
3942
setupUi( this );
4043

4144
connect( btnTextColor, SIGNAL( clicked() ), this, SLOT( changeTextColor() ) );
@@ -44,6 +47,7 @@ QgsLabelingGui::QgsLabelingGui( QgsPalLabeling* lbl, QgsVectorLayer* layer, QgsM
4447
connect( btnBufferColor, SIGNAL( clicked() ), this, SLOT( changeBufferColor() ) );
4548
connect( spinBufferSize, SIGNAL( valueChanged( double ) ), this, SLOT( updatePreview() ) );
4649
connect( btnEngineSettings, SIGNAL( clicked() ), this, SLOT( showEngineConfigDialog() ) );
50+
connect( btnExpression, SIGNAL(clicked()), this, SLOT( showExpressionDialog()));
4751

4852
// set placement methods page based on geometry type
4953
switch ( layer->geometryType() )
@@ -61,19 +65,27 @@ QgsLabelingGui::QgsLabelingGui( QgsPalLabeling* lbl, QgsVectorLayer* layer, QgsM
6165
Q_ASSERT( 0 && "NOOOO!" );
6266
}
6367

68+
//mTabWidget->setEnabled( chkEnableLabeling->isChecked() );
6469
chkMergeLines->setEnabled( layer->geometryType() == QGis::Line );
6570
chkAddDirectionSymbol->setEnabled( layer->geometryType() == QGis::Line );
6671
label_19->setEnabled( layer->geometryType() != QGis::Point );
6772
mMinSizeSpinBox->setEnabled( layer->geometryType() != QGis::Point );
6873

69-
populateFieldNames();
70-
7174
// load labeling settings from layer
7275
QgsPalLayerSettings lyr;
7376
lyr.readFromLayer( layer );
74-
77+
populateFieldNames();
7578
populateDataDefinedCombos( lyr );
7679

80+
chkEnableLabeling->setChecked( lyr.enabled );
81+
mTabWidget->setEnabled( lyr.enabled );
82+
cboFieldName->setEnabled( lyr.enabled );
83+
btnExpression->setEnabled( lyr.enabled );
84+
85+
//Add the current expression to the bottom of the list.
86+
if (lyr.isExpression && !lyr.fieldName.isEmpty())
87+
cboFieldName->addItem(lyr.fieldName);
88+
7789
// placement
7890
int distUnitIndex = lyr.distInMapUnits ? 1 : 0;
7991
switch ( lyr.placement )
@@ -200,18 +212,22 @@ QgsLabelingGui::~QgsLabelingGui()
200212

201213
void QgsLabelingGui::apply()
202214
{
203-
layerSettings().writeToLayer( mLayer );
204-
// trigger refresh
205-
if ( mMapCanvas )
206-
{
207-
mMapCanvas->refresh();
208-
}
215+
QgsPalLayerSettings settings = layerSettings();
216+
settings.writeToLayer( mLayer );
217+
// trigger refresh
218+
if ( mMapCanvas )
219+
{
220+
mMapCanvas->refresh();
221+
}
209222
}
210223

211224
QgsPalLayerSettings QgsLabelingGui::layerSettings()
212225
{
213226
QgsPalLayerSettings lyr;
214227
lyr.fieldName = cboFieldName->currentText();
228+
// Check if we are an expression. Also treats expressions with just a column name as non expressions,
229+
// this saves time later so we don't have to parse the expression tree.
230+
lyr.isExpression = mLayer->fieldNameIndex( lyr.fieldName ) == -1 && !lyr.fieldName.isEmpty();
215231

216232
lyr.dist = 0;
217233
lyr.placementFlags = 0;
@@ -328,7 +344,6 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
328344
return lyr;
329345
}
330346

331-
332347
void QgsLabelingGui::populateFieldNames()
333348
{
334349
const QgsFieldMap& fields = mLayer->pendingFields();
@@ -477,6 +492,23 @@ void QgsLabelingGui::showEngineConfigDialog()
477492
dlg.exec();
478493
}
479494

495+
void QgsLabelingGui::showExpressionDialog()
496+
{
497+
//TODO extract this out to a dialog.
498+
QgsExpressionBuilderDialog dlg( mLayer, cboFieldName->currentText() , this );
499+
dlg.setWindowTitle( tr("Expression based label") );
500+
if ( dlg.exec() == QDialog::Accepted )
501+
{
502+
QString expression = dlg.expressionBuilder()->getExpressionString();
503+
//Only add the expression if the user has entered some text.
504+
if (!expression.isEmpty())
505+
{
506+
cboFieldName->addItem(expression);
507+
cboFieldName->setCurrentIndex(cboFieldName->count() - 1);
508+
}
509+
}
510+
}
511+
480512
void QgsLabelingGui::updateUi()
481513
{
482514
// enable/disable scale-based, buffer, decimals

src/app/qgslabelinggui.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class QgsLabelingGui : public QDialog, private Ui::QgsLabelingGuiBase
4141
void changeTextColor();
4242
void changeTextFont();
4343
void showEngineConfigDialog();
44+
void showExpressionDialog();
4445
void changeBufferColor();
4546

4647
void updateUi();

src/core/qgsexpression.cpp

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -375,40 +375,46 @@ typedef QgsExpression::FunctionDef FnDef;
375375
FnDef QgsExpression::BuiltinFunctions[] =
376376
{
377377
// math
378-
FnDef( "sqrt", 1, fcnSqrt ),
379-
FnDef( "sin", 1, fcnSin ),
380-
FnDef( "cos", 1, fcnCos ),
381-
FnDef( "tan", 1, fcnTan ),
382-
FnDef( "asin", 1, fcnAsin ),
383-
FnDef( "acos", 1, fcnAcos ),
384-
FnDef( "atan", 1, fcnAtan ),
385-
FnDef( "atan2", 2, fcnAtan2 ),
386-
FnDef( "exp", 1, fcnExp ),
387-
FnDef( "ln", 1, fcnLn ),
388-
FnDef( "log10", 1, fcnLog10 ),
389-
FnDef( "log", 2, fcnLog ),
378+
FnDef( "sqrt", 1, fcnSqrt, "Math"),
379+
FnDef( "sin", 1, fcnSin, "Math" ),
380+
FnDef( "cos", 1, fcnCos, "Math"),
381+
FnDef( "tan", 1, fcnTan, "Math" ),
382+
FnDef( "asin", 1, fcnAsin, "Math" ),
383+
FnDef( "acos", 1, fcnAcos, "Math" ),
384+
FnDef( "atan", 1, fcnAtan, "Math" ),
385+
FnDef( "atan2", 2, fcnAtan2, "Math" ),
386+
FnDef( "exp", 1, fcnExp, "Math" ),
387+
FnDef( "ln", 1, fcnLn, "Math" ),
388+
FnDef( "log10", 1, fcnLog10, "Math" ),
389+
FnDef( "log", 2, fcnLog, "Math" ),
390390
// casts
391-
FnDef( "toint", 1, fcnToInt ),
392-
FnDef( "toreal", 1, fcnToReal ),
393-
FnDef( "tostring", 1, fcnToString ),
391+
FnDef( "toint", 1, fcnToInt, "Conversions" ),
392+
FnDef( "toreal", 1, fcnToReal, "Conversions" ),
393+
FnDef( "tostring", 1, fcnToString, "Conversions" ),
394394
// string manipulation
395-
FnDef( "lower", 1, fcnLower ),
396-
FnDef( "upper", 1, fcnUpper ),
397-
FnDef( "length", 1, fcnLength ),
398-
FnDef( "replace", 3, fcnReplace ),
399-
FnDef( "regexp_replace", 3, fcnRegexpReplace ),
400-
FnDef( "substr", 3, fcnSubstr ),
395+
FnDef( "lower", 1, fcnLower, "String", "<b>Convert to lower case</b> "\
396+
"<br> Converts a string to lower case letters. " \
397+
"<br> <i>Usage:</i><br>lower('HELLO WORLD') will return 'hello world'"),
398+
FnDef( "upper", 1, fcnUpper, "String" , "<b>Convert to upper case</b> "\
399+
"<br> Converts a string to upper case letters. " \
400+
"<br> <i>Usage:</i><br>upper('hello world') will return 'HELLO WORLD'"),
401+
FnDef( "length", 1, fcnLength, "String", "<b>Length of string</b> "\
402+
"<br> Returns the legnth of a string. " \
403+
"<br> <i>Usage:</i><br>length('hello') will return 5"),
404+
FnDef( "replace", 3, fcnReplace, "String", "<b>Replace a section of a string.</b> "),
405+
FnDef( "regexp_replace", 3, fcnRegexpReplace, "String" ),
406+
FnDef( "substr", 3, fcnSubstr, "String" ),
401407
// geometry accessors
402-
FnDef( "xat", 1, fcnXat, true ),
403-
FnDef( "yat", 1, fcnYat, true ),
408+
FnDef( "xat", 1, fcnXat, "Geometry", "", true ),
409+
FnDef( "yat", 1, fcnYat, "Geometry", "", true ),
410+
FnDef( "$area", 0, fcnGeomArea, "Geometry", "", true ),
411+
FnDef( "$length", 0, fcnGeomLength, "Geometry", "", true ),
412+
FnDef( "$perimeter", 0, fcnGeomPerimeter, "Geometry", "", true ),
413+
FnDef( "$x", 0, fcnX, "Geometry", "", true ),
414+
FnDef( "$y", 0, fcnY, "Geometry", "" , true ),
404415
// special columns
405-
FnDef( "$rownum", 0, fcnRowNumber ),
406-
FnDef( "$area", 0, fcnGeomArea, true ),
407-
FnDef( "$length", 0, fcnGeomLength, true ),
408-
FnDef( "$perimeter", 0, fcnGeomPerimeter, true ),
409-
FnDef( "$x", 0, fcnX, true ),
410-
FnDef( "$y", 0, fcnY, true ),
411-
FnDef( "$id", 0, fcnFeatureId ),
416+
FnDef( "$rownum", 0, fcnRowNumber, "Record" ),
417+
FnDef( "$id", 0, fcnFeatureId, "Record")
412418
};
413419

414420

@@ -419,7 +425,7 @@ bool QgsExpression::isFunctionName( QString name )
419425

420426
int QgsExpression::functionIndex( QString name )
421427
{
422-
int count = sizeof( BuiltinFunctions ) / sizeof( FunctionDef );
428+
int count = functionCount();
423429
for ( int i = 0; i < count; i++ )
424430
{
425431
if ( QString::compare( name, BuiltinFunctions[i].mName, Qt::CaseInsensitive ) == 0 )
@@ -428,6 +434,11 @@ int QgsExpression::functionIndex( QString name )
428434
return -1;
429435
}
430436

437+
int QgsExpression::functionCount()
438+
{
439+
return ( sizeof( BuiltinFunctions ) / sizeof( FunctionDef) );
440+
}
441+
431442

432443
QgsExpression::QgsExpression( const QString& expr )
433444
: mExpression( expr ), mRowNumber( 0 ), mCalc( NULL )

src/core/qgsexpression.h

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ The expressions try to follow both syntax and semantics of SQL expressions.
3030
3131
Usage:
3232
33-
QgsExpression exp("gid*2 > 10 and type not in ('D','F');
33+
QgsExpression exp("gid*2 > 10 and type not in ('D','F'));
3434
if (exp.hasParserError())
3535
{
3636
// show error message with parserErrorString() and exit
@@ -170,12 +170,20 @@ class CORE_EXPORT QgsExpression
170170

171171
struct FunctionDef
172172
{
173-
FunctionDef( QString fnname, int params, FcnEval fcn, bool usesGeometry = false )
174-
: mName( fnname ), mParams( params ), mFcn( fcn ), mUsesGeometry( usesGeometry ) {}
173+
FunctionDef( QString fnname, int params, FcnEval fcn, QString group, QString helpText = "", bool usesGeometry = false )
174+
: mName( fnname ), mParams( params ), mFcn( fcn ), mUsesGeometry( usesGeometry ), mGroup( group ), mHelpText( helpText ) {}
175+
/** The name of the function. */
175176
QString mName;
177+
/** The number of parameters this function takes. */
176178
int mParams;
179+
/** Pointer to fucntion. */
177180
FcnEval mFcn;
181+
/** Does this function use a geometry object. */
178182
bool mUsesGeometry;
183+
/** The group the function belongs to. */
184+
QString mGroup;
185+
/** The help text for the function. */
186+
QString mHelpText;
179187
};
180188

181189
static FunctionDef BuiltinFunctions[];
@@ -186,6 +194,11 @@ class CORE_EXPORT QgsExpression
186194
// return index of the function in BuiltinFunctions array
187195
static int functionIndex( QString name );
188196

197+
/** Returns the number of functions defined in the parser
198+
* @return The number of function defined in the parser.
199+
*/
200+
static int functionCount();
201+
189202
//! return quoted column reference (in double quotes)
190203
static QString quotedColumnRef( QString name ) { return QString( "\"%1\"" ).arg( name.replace( "\"", "\"\"" ) ); }
191204

src/core/qgslabel.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,9 @@ QString QgsLabel::fieldValue( int attr, QgsFeature &feature )
8484
}
8585

8686
void QgsLabel::renderLabel( QgsRenderContext &renderContext,
87-
QgsFeature &feature,
88-
bool selected,
87+
QgsFeature &feature, bool selected,
8988
QgsLabelAttributes *classAttributes )
9089
{
91-
Q_UNUSED( classAttributes );
9290
if ( mLabelAttributes->selectedOnly() && !selected )
9391
return;
9492

0 commit comments

Comments
 (0)