Skip to content

Commit 847204e

Browse files
committedOct 20, 2011
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

‎src/core/qgspallabeling.cpp

Lines changed: 72 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@
4141
#include "qgsdiagram.h"
4242
#include "qgsdiagramrendererv2.h"
4343
#include "qgslabelsearchtree.h"
44+
#include "qgsexpression.h"
45+
4446
#include <qgslogger.h>
4547
#include <qgsvectorlayer.h>
4648
#include <qgsmaplayerregistry.h>
4749
#include <qgsvectordataprovider.h>
4850
#include <qgsgeometry.h>
4951
#include <qgsmaprenderer.h>
50-
52+
#include <QMessageBox>
5153

5254
using namespace pal;
5355

@@ -131,7 +133,7 @@ class QgsPalGeometry : public PalGeometry
131133
// -------------
132134

133135
QgsPalLayerSettings::QgsPalLayerSettings()
134-
: palLayer( NULL ), fontMetrics( NULL ), ct( NULL ), extentGeom( NULL )
136+
: palLayer( NULL ), fontMetrics( NULL ), ct( NULL ), extentGeom( NULL ), expression( NULL )
135137
{
136138
placement = AroundPoint;
137139
placementFlags = 0;
@@ -163,6 +165,7 @@ QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
163165
{
164166
// copy only permanent stuff
165167
fieldName = s.fieldName;
168+
isExpression = s.isExpression;
166169
placement = s.placement;
167170
placementFlags = s.placementFlags;
168171
textFont = s.textFont;
@@ -192,6 +195,7 @@ QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
192195
fontMetrics = NULL;
193196
ct = NULL;
194197
extentGeom = NULL;
198+
expression = NULL;
195199
}
196200

197201

@@ -201,10 +205,19 @@ QgsPalLayerSettings::~QgsPalLayerSettings()
201205

202206
delete fontMetrics;
203207
delete ct;
204-
208+
delete expression;
205209
delete extentGeom;
206210
}
207211

212+
QgsExpression* QgsPalLayerSettings::getLabelExpression()
213+
{
214+
if (expression == NULL)
215+
{
216+
expression = new QgsExpression( fieldName );
217+
}
218+
return expression;
219+
}
220+
208221
static QColor _readColor( QgsVectorLayer* layer, QString property )
209222
{
210223
int r = layer->customProperty( property + "R" ).toInt();
@@ -290,6 +303,7 @@ void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer )
290303
return; // there's no information available
291304

292305
fieldName = layer->customProperty( "labeling/fieldName" ).toString();
306+
isExpression = layer->customProperty( "labeling/isExpression").toBool();
293307
placement = ( Placement ) layer->customProperty( "labeling/placement" ).toInt();
294308
placementFlags = layer->customProperty( "labeling/placementFlags" ).toUInt();
295309
QString fontFamily = layer->customProperty( "labeling/fontFamily" ).toString();
@@ -328,6 +342,7 @@ void QgsPalLayerSettings::writeToLayer( QgsVectorLayer* layer )
328342
layer->setCustomProperty( "labeling", "pal" );
329343

330344
layer->setCustomProperty( "labeling/fieldName", fieldName );
345+
layer->setCustomProperty( "labeling/isExpression", isExpression );
331346
layer->setCustomProperty( "labeling/placement", placement );
332347
layer->setCustomProperty( "labeling/placementFlags", ( unsigned int )placementFlags );
333348

@@ -447,25 +462,42 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString t
447462
labelY = qAbs( ptSize.y() - ptZero.y() );
448463
}
449464

450-
void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext& context )
465+
void QgsPalLayerSettings::registerFeature(QgsVectorLayer* layer, QgsFeature& f, const QgsRenderContext& context )
451466
{
452-
453467
QString labelText;
454-
if ( formatNumbers == true
455-
&& ( f.attributeMap()[fieldIndex].type() == QVariant::Int || f.attributeMap()[fieldIndex].type() == QVariant::Double ) )
468+
// Check to see if we are a expression string.
469+
if (isExpression)
456470
{
457-
QString numberFormat;
458-
double d = f.attributeMap()[fieldIndex].toDouble();
459-
if ( d > 0 && plusSign == true )
471+
QgsExpression* exp = getLabelExpression();
472+
if ( exp->hasParserError() )
473+
{
474+
QgsDebugMsg("PASER HAS ERROR:" + exp->parserErrorString());
475+
return;
476+
}
477+
QVariant result = exp->evaluate(&f,layer->dataProvider()->fields());
478+
QgsDebugMsg("VALUE = " + result.toString());
479+
if (exp->hasEvalError())
460480
{
461-
numberFormat.append( "+" );
481+
QgsDebugMsg("Expression Label Error = " + exp->evalErrorString());
482+
return;
462483
}
463-
numberFormat.append( "%1" );
464-
labelText = numberFormat.arg( d, 0, 'f', decimals );
484+
labelText = result.toString();
485+
}
486+
else if ( formatNumbers == true && ( f.attributeMap()[fieldIndex].type() == QVariant::Int ||
487+
f.attributeMap()[fieldIndex].type() == QVariant::Double ) )
488+
{
489+
QString numberFormat;
490+
double d = f.attributeMap()[fieldIndex].toDouble();
491+
if ( d > 0 && plusSign == true )
492+
{
493+
numberFormat.append( "+" );
494+
}
495+
numberFormat.append( "%1" );
496+
labelText = numberFormat.arg( d, 0, 'f', decimals );
465497
}
466498
else
467499
{
468-
labelText = f.attributeMap()[fieldIndex].toString();
500+
labelText = f.attributeMap()[fieldIndex].toString();
469501
}
470502

471503
double labelX, labelY; // will receive label size
@@ -631,7 +663,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
631663
// record the created geometry - it will be deleted at the end.
632664
geometries.append( lbl );
633665

634-
// register feature to the layer
666+
// feature to the layer
635667
try
636668
{
637669
if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData(),
@@ -742,6 +774,7 @@ bool QgsPalLabeling::willUseLayer( QgsVectorLayer* layer )
742774

743775
int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QSet<int>& attrIndices, QgsRenderContext& ctx )
744776
{
777+
QgsDebugMsg("PREPARE LAYER");
745778
Q_ASSERT( mMapRenderer != NULL );
746779

747780
// start with a temporary settings class, find out labeling info
@@ -751,11 +784,28 @@ int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QSet<int>& attrIndices,
751784
if ( !lyrTmp.enabled )
752785
return 0;
753786

754-
// find out which field will be needed
755-
int fldIndex = layer->fieldNameIndex( lyrTmp.fieldName );
756-
if ( fldIndex == -1 )
757-
return 0;
758-
attrIndices.insert( fldIndex );
787+
788+
int fldIndex ;
789+
if(lyrTmp.isExpression)
790+
{
791+
if (lyrTmp.fieldName.isEmpty())
792+
return 0;
793+
QgsExpression exp( lyrTmp.fieldName );
794+
foreach(QString name, exp.referencedColumns() )
795+
{
796+
QgsDebugMsg("REFERENCED COLUMN = " + name );
797+
fldIndex = layer->fieldNameIndex( name );
798+
attrIndices.insert(fldIndex);
799+
}
800+
}
801+
else
802+
{
803+
// If we aren't an expression, we check to see if we can find the column.
804+
fldIndex = layer->fieldNameIndex( lyrTmp.fieldName );
805+
if ( fldIndex == -1)
806+
return 0;
807+
attrIndices.insert( fldIndex );
808+
}
759809

760810
//add indices of data defined fields
761811
QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator dIt = lyrTmp.dataDefinedProperties.constBegin();
@@ -847,7 +897,7 @@ int QgsPalLabeling::addDiagramLayer( QgsVectorLayer* layer, QgsDiagramLayerSetti
847897
void QgsPalLabeling::registerFeature( QgsVectorLayer* layer, QgsFeature& f, const QgsRenderContext& context )
848898
{
849899
QgsPalLayerSettings& lyr = mActiveLayers[layer];
850-
lyr.registerFeature( f, context );
900+
lyr.registerFeature(layer, f, context );
851901
}
852902

853903
void QgsPalLabeling::registerDiagramFeature( QgsVectorLayer* layer, QgsFeature& feat, const QgsRenderContext& context )
@@ -901,7 +951,7 @@ void QgsPalLabeling::registerDiagramFeature( QgsVectorLayer* layer, QgsFeature&
901951
}
902952
}
903953

904-
// register feature to the layer
954+
// feature to the layer
905955
int ddColX = layerIt.value().xPosColumn;
906956
int ddColY = layerIt.value().yPosColumn;
907957
double ddPosX = 0.0;
@@ -1279,7 +1329,6 @@ void QgsPalLabeling::drawLabel( pal::LabelPosition* label, QPainter* painter, co
12791329

12801330
// TODO: optimize access :)
12811331
const QgsPalLayerSettings& lyr = layer( QString::fromUtf8( label->getLayerName() ) );
1282-
12831332
QString text = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->text();
12841333
QString txt = ( label->getPartId() == -1 ? text : QString( text[label->getPartId()] ) );
12851334

‎src/core/qgspallabeling.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@ namespace pal
4646

4747
class QgsMapToPixel;
4848
class QgsFeature;
49-
#include "qgspoint.h"
5049

50+
#include "qgspoint.h"
5151
#include "qgsmaprenderer.h" // definition of QgsLabelingEngineInterface
52+
#include "qgsexpression.h"
5253

5354
class QgsPalGeometry;
5455
class QgsVectorLayer;
@@ -98,6 +99,15 @@ class CORE_EXPORT QgsPalLayerSettings
9899
};
99100

100101
QString fieldName;
102+
103+
/** Is this label made from a expression string eg FieldName || 'mm'
104+
*/
105+
bool isExpression;
106+
107+
/** Returns the QgsExpression for this label settings.
108+
*/
109+
QgsExpression* getLabelExpression();
110+
101111
Placement placement;
102112
unsigned int placementFlags;
103113
QFont textFont;
@@ -128,7 +138,7 @@ class CORE_EXPORT QgsPalLayerSettings
128138
void calculateLabelSize( const QFontMetricsF* fm, QString text, double& labelX, double& labelY );
129139

130140
// implementation of register feature hook
131-
void registerFeature( QgsFeature& f, const QgsRenderContext& context );
141+
void registerFeature(QgsVectorLayer* layer, QgsFeature& f, const QgsRenderContext& context );
132142

133143
void readFromLayer( QgsVectorLayer* layer );
134144
void writeToLayer( QgsVectorLayer* layer );
@@ -161,6 +171,7 @@ class CORE_EXPORT QgsPalLayerSettings
161171
/**Checks if a feature is larger than a minimum size (in mm)
162172
@return true if above size, false if below*/
163173
bool checkMinimumSizeMM( const QgsRenderContext& ct, QgsGeometry* geom, double minSize ) const;
174+
QgsExpression* expression;
164175
};
165176

166177
class CORE_EXPORT QgsLabelCandidate

‎src/gui/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ qgstextannotationitem.cpp
7070
qgsvertexmarker.cpp
7171
qgsludialog.cpp
7272
qgssearchquerybuilder.cpp
73+
qgsexpressionbuilderwidget.cpp
74+
qgsexpressionbuilderdialog.cpp
7375
qgsquerybuilder.cpp
7476
)
7577

@@ -126,6 +128,8 @@ qgsludialog.h
126128
qgsprojectbadlayerguihandler.h
127129
qgslonglongvalidator.h
128130
qgssearchquerybuilder.h
131+
qgsexpressionbuilderwidget.h
132+
qgsexpressionbuilderdialog.h
129133
qgsquerybuilder.h
130134
)
131135

@@ -159,6 +163,8 @@ qgsmaptip.h
159163
qgssearchquerybuilder.h
160164
qgsattributeeditor.h
161165
qgsfieldvalidator.h
166+
qgsexpressionbuilderwidget.h
167+
qgsexpressionbuilderdialog.h
162168

163169
attributetable/qgsattributetablemodel.h
164170
attributetable/qgsattributetablememorymodel.h
@@ -175,6 +181,8 @@ ${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsmessageviewer.h
175181
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgscredentialdialog.h
176182
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsprojectionselectorbase.h
177183
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsquerybuilderbase.h
184+
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsexpressionbuilderwidget.h
185+
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsexpressionbuilderdialogbase.h
178186
)
179187

180188
INCLUDE_DIRECTORIES(
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/***************************************************************************
2+
qgisexpressionbuilderdialog.h - A genric expression string builder dialog.
3+
--------------------------------------
4+
Date : 29-May-2011
5+
Copyright : (C) 2011 by Nathan Woodrow
6+
Email : woodrow.nathan at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgsexpressionbuilderdialog.h"
17+
#include <QSettings>
18+
19+
QgsExpressionBuilderDialog::QgsExpressionBuilderDialog( QgsVectorLayer* layer, QString startText, QWidget* parent)
20+
:QDialog( parent )
21+
{
22+
setupUi( this );
23+
24+
QPushButton* okButuon = buttonBox->button(QDialogButtonBox::Ok);
25+
connect(builder, SIGNAL(expressionParsed(bool)), okButuon, SLOT(setEnabled(bool)));
26+
27+
builder->setLayer( layer );
28+
builder->setExpressionString(startText);
29+
builder->loadFieldNames();
30+
31+
QSettings settings;
32+
restoreGeometry( settings.value( "/Windows/ExpressionBuilderDialog/geometry" ).toByteArray() );
33+
}
34+
35+
QgsExpressionBuilderWidget* QgsExpressionBuilderDialog::expressionBuilder()
36+
{
37+
return builder;
38+
}
39+
40+
void QgsExpressionBuilderDialog::setExpressionText( QString text )
41+
{
42+
builder->setExpressionString( text );
43+
}
44+
45+
void QgsExpressionBuilderDialog::closeEvent( QCloseEvent *event )
46+
{
47+
QDialog::closeEvent( event );
48+
49+
// TODO Work out why this is not working yet.
50+
QSettings settings;
51+
settings.setValue( "/Windows/ExpressionBuilderDialog/geometry", saveGeometry() );
52+
}

‎src/gui/qgsexpressionbuilderdialog.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/***************************************************************************
2+
qgisexpressionbuilderdialog.h - A genric expression string builder dialog.
3+
--------------------------------------
4+
Date : 29-May-2011
5+
Copyright : (C) 2011 by Nathan Woodrow
6+
Email : woodrow.nathan at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSEXPRESSIONBUILDERDIALOG_H
17+
#define QGSEXPRESSIONBUILDERDIALOG_H
18+
19+
#include <QDialog>
20+
#include "ui_qgsexpressionbuilderdialogbase.h"
21+
22+
class QgsExpressionBuilderDialog : public QDialog, private Ui::QgsExpressionBuilderDialogBase
23+
{
24+
public:
25+
QgsExpressionBuilderDialog( QgsVectorLayer* layer, QString startText = "", QWidget* parent = NULL);
26+
27+
/** The builder widget that is used by the dialog */
28+
QgsExpressionBuilderWidget* expressionBuilder();
29+
30+
void setExpressionText( QString text );
31+
32+
protected:
33+
/**
34+
* Handle closing of the window
35+
* @param event unused
36+
*/
37+
void closeEvent( QCloseEvent * event );
38+
};
39+
40+
#endif
Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
/***************************************************************************
2+
qgisexpressionbuilderwidget.cpp - A genric expression string builder widget.
3+
--------------------------------------
4+
Date : 29-May-2011
5+
Copyright : (C) 2011 by Nathan Woodrow
6+
Email : woodrow.nathan at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgsexpressionbuilderwidget.h"
17+
#include "qgslogger.h"
18+
#include "qgsexpression.h"
19+
#include "qgsmessageviewer.h"
20+
21+
#include <QMenu>
22+
23+
QgsExpressionBuilderWidget::QgsExpressionBuilderWidget(QWidget *parent)
24+
: QWidget(parent)
25+
{
26+
setupUi(this);
27+
28+
mValueListWidget->hide();
29+
mValueListLabel->hide();
30+
31+
mModel = new QStandardItemModel( );
32+
mProxyModel = new QgsExpressionItemSearhProxy();
33+
mProxyModel->setSourceModel( mModel );
34+
expressionTree->setModel( mProxyModel );
35+
36+
expressionTree->setContextMenuPolicy( Qt::CustomContextMenu );
37+
connect( expressionTree, SIGNAL( customContextMenuRequested( const QPoint & ) ), this, SLOT( showContextMenu( const QPoint & ) ) );
38+
connect( btnPlusPushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
39+
connect( btnMinusPushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
40+
connect( btnDividePushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
41+
connect( btnMultiplyPushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
42+
connect( btnExpButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
43+
connect( btnConcatButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
44+
connect( btnOpenBracketPushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
45+
connect( btnCloseBracketPushButton, SIGNAL(pressed()), this, SLOT(operatorButtonClicked()));
46+
47+
48+
// TODO Can we move this stuff to QgsExpression, like the functions?
49+
this->registerItem("Operators","+"," + ");
50+
this->registerItem("Operators","-"," -");
51+
this->registerItem("Operators","*"," * ");
52+
this->registerItem("Operators","/"," / ");
53+
this->registerItem("Operators","%"," % ");
54+
this->registerItem("Operators","^"," ^ ");
55+
this->registerItem("Operators","="," = ");
56+
this->registerItem("Operators",">"," > ");
57+
this->registerItem("Operators","<"," < ");
58+
this->registerItem("Operators","<>"," <> ");
59+
this->registerItem("Operators","<="," <= ");
60+
this->registerItem("Operators",">="," >= ");
61+
this->registerItem("Operators","||"," || ","<b>|| (String Concatenation)</b> "\
62+
"<br> Joins two values together into a string " \
63+
"<br> <i>Usage:</i><br>'Dia' || Diameter");
64+
this->registerItem("Operators","LIKE"," LIKE ");
65+
this->registerItem("Operators","ILIKE"," ILIKE ");
66+
this->registerItem("Operators","IS"," IS NOT ");
67+
this->registerItem("Operators","OR"," OR ");
68+
this->registerItem("Operators","AND"," AND ");
69+
this->registerItem("Operators","NOT"," NOT ");
70+
71+
72+
// Load the fuctions from the QgsExpression class
73+
int count = QgsExpression::functionCount();
74+
for ( int i = 0; i < count; i++ )
75+
{
76+
QgsExpression::FunctionDef func = QgsExpression::BuiltinFunctions[i];
77+
QString name = func.mName;
78+
if ( func.mParams >= 1 )
79+
name += "(";
80+
this->registerItem(func.mGroup,func.mName, " " + name + " ", func.mHelpText);
81+
};
82+
}
83+
84+
85+
QgsExpressionBuilderWidget::~QgsExpressionBuilderWidget()
86+
{
87+
88+
}
89+
90+
void QgsExpressionBuilderWidget::setLayer( QgsVectorLayer *layer )
91+
{
92+
mLayer = layer;
93+
}
94+
95+
void QgsExpressionBuilderWidget::on_expressionTree_clicked(const QModelIndex &index)
96+
{
97+
// Get the item
98+
QModelIndex idx = mProxyModel->mapToSource(index);
99+
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
100+
if ( item == 0 )
101+
return;
102+
103+
// Loading field values are handled with a
104+
// right click so we just show the help.
105+
if (item->getItemType() == QgsExpressionItem::Field)
106+
{
107+
txtHelpText->setText( tr("Double click to add field name to expression string. <br> " \
108+
"Or right click to select loading value options then " \
109+
"double click an item in the value list to add it to the expression string."));
110+
txtHelpText->setToolTip(txtHelpText->text());
111+
}
112+
else
113+
{
114+
// Show the help for the current item.
115+
mValueListWidget->hide();
116+
mValueListLabel->hide();
117+
mValueListWidget->clear();
118+
txtHelpText->setText(item->getHelpText());
119+
txtHelpText->setToolTip(txtHelpText->text());
120+
}
121+
}
122+
123+
void QgsExpressionBuilderWidget::on_expressionTree_doubleClicked(const QModelIndex &index)
124+
{
125+
QModelIndex idx = mProxyModel->mapToSource(index);
126+
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
127+
if (item == 0)
128+
return;
129+
130+
// Don't handle the double click it we are on a header node.
131+
if (item->getItemType() == QgsExpressionItem::Header)
132+
return;
133+
134+
// Insert the expression text.
135+
txtExpressionString->insertPlainText(item->getExpressionText());
136+
}
137+
138+
void QgsExpressionBuilderWidget::loadFieldNames()
139+
{
140+
// TODO We should really return a error the user of the widget that
141+
// the there is no layer set.
142+
if ( !mLayer )
143+
return;
144+
145+
const QgsFieldMap fieldMap = mLayer->pendingFields();
146+
QgsFieldMap::const_iterator fieldIt = fieldMap.constBegin();
147+
for ( ; fieldIt != fieldMap.constEnd(); ++fieldIt )
148+
{
149+
QString fieldName = fieldIt.value().name();
150+
this->registerItem("Fields", fieldName, " " + fieldName + " ","", QgsExpressionItem::Field);
151+
}
152+
}
153+
154+
void QgsExpressionBuilderWidget::fillFieldValues(int fieldIndex, int countLimit)
155+
{
156+
// TODO We should really return a error the user of the widget that
157+
// the there is no layer set.
158+
if ( !mLayer )
159+
return;
160+
161+
// TODO We should thread this so that we don't hold the user up if the layer is massive.
162+
mValueListWidget->clear();
163+
mValueListWidget->setUpdatesEnabled( false );
164+
mValueListWidget->blockSignals( true );
165+
166+
QList<QVariant> values;
167+
mLayer->uniqueValues( fieldIndex, values, countLimit );
168+
foreach(QVariant value, values)
169+
{
170+
mValueListWidget->addItem(value.toString());
171+
}
172+
173+
mValueListWidget->setUpdatesEnabled( true );
174+
mValueListWidget->blockSignals( false );
175+
}
176+
177+
void QgsExpressionBuilderWidget::registerItem(QString group,
178+
QString label,
179+
QString expressionText,
180+
QString helpText,
181+
QgsExpressionItem::ItemType type)
182+
{
183+
QgsExpressionItem* item = new QgsExpressionItem(label,expressionText, helpText, type);
184+
// Look up the group and insert the new function.
185+
if (mExpressionGroups.contains(group))
186+
{
187+
QgsExpressionItem* groupNode = mExpressionGroups.value(group);
188+
groupNode->appendRow(item);
189+
}
190+
else
191+
{
192+
// If the group doesn't exsit yet we make it first.
193+
QgsExpressionItem* newgroupNode = new QgsExpressionItem(group,"", QgsExpressionItem::Header);
194+
newgroupNode->appendRow(item);
195+
mModel->appendRow(newgroupNode);
196+
mExpressionGroups.insert(group , newgroupNode );
197+
}
198+
}
199+
200+
QString QgsExpressionBuilderWidget::getExpressionString()
201+
{
202+
return this->txtExpressionString->toPlainText();
203+
}
204+
205+
void QgsExpressionBuilderWidget::setExpressionString(const QString expressionString)
206+
{
207+
this->txtExpressionString->setPlainText(expressionString);
208+
}
209+
210+
void QgsExpressionBuilderWidget::on_txtExpressionString_textChanged()
211+
{
212+
QString text = this->txtExpressionString->toPlainText();
213+
214+
// If the string is empty the expression will still "fail" although
215+
// we don't show the user an error as it will be confusing.
216+
if ( text.isEmpty() )
217+
{
218+
this->lblPreview->setText("");
219+
this->lblPreview->setStyleSheet("");
220+
this->txtExpressionString->setToolTip("");
221+
this->lblPreview->setToolTip("");
222+
// Return false for isVaild because a null expression is still invaild.
223+
emit expressionParsed(false);
224+
return;
225+
}
226+
227+
QgsExpression exp( text );
228+
229+
// TODO We could do this without a layer.
230+
// Maybe just calling exp.evaluate()?
231+
if ( mLayer )
232+
{
233+
// TODO We should really cache the feature.
234+
QgsFeature feature;
235+
mLayer->featureAtId( 0 , feature );
236+
QVariant value = exp.evaluate( &feature, mLayer->pendingFields() );
237+
238+
if (!exp.hasEvalError())
239+
lblPreview->setText( value.toString() );
240+
}
241+
242+
if ( exp.hasParserError() || exp.hasEvalError())
243+
{
244+
QString tooltip = "<b>Parser Error:</b> <br>" + exp.parserErrorString();
245+
if (exp.hasEvalError())
246+
tooltip += "<br><br> <b>Eval Error:</b> <br>" + exp.evalErrorString();
247+
248+
this->lblPreview->setText( "Expression is invaild <a href=""more"">(more info)</a>" );
249+
this->lblPreview->setStyleSheet("color: rgba(255, 6, 10, 255);");
250+
this->txtExpressionString->setToolTip(tooltip);
251+
this->lblPreview->setToolTip(tooltip);
252+
emit expressionParsed(false);
253+
return;
254+
}
255+
else
256+
{
257+
this->lblPreview->setStyleSheet("");
258+
this->txtExpressionString->setToolTip("");
259+
this->lblPreview->setToolTip("");
260+
emit expressionParsed(true);
261+
}
262+
}
263+
264+
void QgsExpressionBuilderWidget::on_txtSearchEdit_textChanged()
265+
{
266+
mProxyModel->setFilterWildcard( txtSearchEdit->text() );
267+
if ( txtSearchEdit->text().isEmpty() )
268+
expressionTree->collapseAll();
269+
else
270+
expressionTree->expandAll();
271+
}
272+
273+
void QgsExpressionBuilderWidget::on_lblPreview_linkActivated(QString link)
274+
{
275+
QgsMessageViewer * mv = new QgsMessageViewer( this );
276+
mv->setWindowTitle( "More info on expression error" );
277+
mv->setMessageAsHtml( this->txtExpressionString->toolTip());
278+
mv->exec();
279+
}
280+
281+
void QgsExpressionBuilderWidget::on_mValueListWidget_itemDoubleClicked(QListWidgetItem *item)
282+
{
283+
txtExpressionString->insertPlainText( " " + item->text() + " " );
284+
}
285+
286+
void QgsExpressionBuilderWidget::operatorButtonClicked()
287+
{
288+
QPushButton* button = dynamic_cast<QPushButton*>( sender() );
289+
txtExpressionString->insertPlainText( " " + button->text() + " " );
290+
}
291+
292+
void QgsExpressionBuilderWidget::showContextMenu( const QPoint & pt)
293+
{
294+
QModelIndex idx = expressionTree->indexAt( pt );
295+
idx = mProxyModel->mapToSource( idx );
296+
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
297+
if ( !item )
298+
return;
299+
300+
if (item->getItemType() == QgsExpressionItem::Field)
301+
{
302+
QMenu* menu = new QMenu( this );
303+
menu->addAction( tr( "Load top 10 unique values" ), this, SLOT( loadSampleValues()) );
304+
menu->addAction( tr( "Load all unique values" ), this, SLOT( loadAllValues() ) );
305+
menu->popup( expressionTree->mapToGlobal( pt ) );
306+
}
307+
}
308+
309+
void QgsExpressionBuilderWidget::loadSampleValues()
310+
{
311+
QModelIndex idx = mProxyModel->mapToSource(expressionTree->currentIndex());
312+
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
313+
// TODO We should really return a error the user of the widget that
314+
// the there is no layer set.
315+
if ( !mLayer )
316+
return;
317+
318+
mValueListWidget->show();
319+
mValueListLabel->show();
320+
int fieldIndex = mLayer->fieldNameIndex(item->text());
321+
fillFieldValues(fieldIndex,10);
322+
}
323+
324+
void QgsExpressionBuilderWidget::loadAllValues()
325+
{
326+
QModelIndex idx = mProxyModel->mapToSource(expressionTree->currentIndex());
327+
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>(mModel->itemFromIndex( idx ));
328+
// TODO We should really return a error the user of the widget that
329+
// the there is no layer set.
330+
if ( !mLayer )
331+
return;
332+
333+
mValueListWidget->show();
334+
mValueListLabel->show();
335+
int fieldIndex = mLayer->fieldNameIndex(item->text());
336+
fillFieldValues(fieldIndex,-1);
337+
}
338+

‎src/gui/qgsexpressionbuilderwidget.h

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/***************************************************************************
2+
qgisexpressionbuilderwidget.h - A genric expression string builder widget.
3+
--------------------------------------
4+
Date : 29-May-2011
5+
Copyright : (C) 2011 by Nathan Woodrow
6+
Email : woodrow.nathan at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSEXPRESSIONBUILDER_H
17+
#define QGSEXPRESSIONBUILDER_H
18+
19+
#include <QWidget>
20+
#include "ui_qgsexpressionbuilder.h"
21+
#include "qgsvectorlayer.h"
22+
23+
#include "QStandardItemModel"
24+
#include "QStandardItem"
25+
#include "QSortFilterProxyModel"
26+
27+
/** Search proxy used to filter the QgsExpressionBuilderWidget tree.
28+
* The default search for a tree model only searches top level this will handle one
29+
* level down
30+
*/
31+
class QgsExpressionItemSearhProxy : public QSortFilterProxyModel
32+
{
33+
public:
34+
QgsExpressionItemSearhProxy() { }
35+
36+
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
37+
{
38+
if (source_parent == qobject_cast<QStandardItemModel*>(sourceModel())->invisibleRootItem()->index())
39+
return true;
40+
41+
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
42+
}
43+
};
44+
45+
/** An expression item that can be used in the QgsExpressionBuilderWidget tree.
46+
*/
47+
class QgsExpressionItem : public QStandardItem
48+
{
49+
public:
50+
enum ItemType
51+
{
52+
Header,
53+
Field,
54+
ExpressionNode
55+
};
56+
57+
QgsExpressionItem(QString label,
58+
QString expressionText,
59+
QString helpText,
60+
QgsExpressionItem::ItemType itemType = ExpressionNode)
61+
: QStandardItem(label)
62+
{
63+
mExpressionText = expressionText;
64+
mHelpText = helpText;
65+
mType = itemType;
66+
}
67+
68+
QgsExpressionItem(QString label,
69+
QString expressionText,
70+
QgsExpressionItem::ItemType itemType = ExpressionNode)
71+
: QStandardItem(label)
72+
{
73+
mExpressionText = expressionText;
74+
mType = itemType;
75+
}
76+
77+
QString getExpressionText() { return mExpressionText; }
78+
79+
/** Get the help text that is associated with this expression item.
80+
*
81+
* @return The help text.
82+
*/
83+
QString getHelpText() { return mHelpText; }
84+
/** Set the help text for the current item
85+
*
86+
* @note The help text can be set as a html string.
87+
*/
88+
void setHelpText(QString helpText) { mHelpText = helpText; }
89+
90+
/** Get the type of expression item eg header, field, ExpressionNode.
91+
*
92+
* @return The QgsExpressionItem::ItemType
93+
*/
94+
QgsExpressionItem::ItemType getItemType() { return mType ; }
95+
96+
private:
97+
QString mExpressionText;
98+
QString mHelpText;
99+
QgsExpressionItem::ItemType mType;
100+
};
101+
102+
/** A reusable widget that can be used to build a expression string.
103+
* See QgsExpressionBuilderDialog for exmaple of usage.
104+
*/
105+
class QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExpressionBuilderWidgetBase {
106+
Q_OBJECT
107+
public:
108+
QgsExpressionBuilderWidget(QWidget *parent);
109+
~QgsExpressionBuilderWidget();
110+
111+
/** Sets layer in order to get the fields and values
112+
* @note this needs to be called before calling loadFieldNames().
113+
*/
114+
void setLayer( QgsVectorLayer* layer );
115+
116+
/** Loads all the field names from the layer.
117+
* @remarks Should this really be public couldn't we just do this for the user?
118+
*/
119+
void loadFieldNames();
120+
121+
/** Gets the expression string that has been set in the expression area.
122+
* @returns The expression as a string. */
123+
QString getExpressionString();
124+
125+
/** Sets the expression string for the widget */
126+
void setExpressionString(const QString expressionString);
127+
128+
/** Registers a node item for the expression builder.
129+
* @param group The group the item will be show in the tree view. If the group doesn't exsit it will be created.
130+
* @param label The label that is show to the user for the item in the tree.
131+
* @param expressionText The text that is inserted into the expression area when the user double clicks on the item.
132+
* @param helpText The help text that the user will see when item is selected.
133+
* @param type The type of the expression item.
134+
*/
135+
void registerItem(QString group, QString label,QString expressionText,
136+
QString helpText = "",
137+
QgsExpressionItem::ItemType type = QgsExpressionItem::ExpressionNode);
138+
139+
public slots:
140+
void on_expressionTree_clicked(const QModelIndex &index);
141+
void on_expressionTree_doubleClicked(const QModelIndex &index);
142+
void on_txtExpressionString_textChanged();
143+
void on_txtSearchEdit_textChanged();
144+
void on_lblPreview_linkActivated(QString link);
145+
void on_mValueListWidget_itemDoubleClicked(QListWidgetItem* item);
146+
void operatorButtonClicked();
147+
void showContextMenu( const QPoint & );
148+
void loadSampleValues();
149+
void loadAllValues();
150+
151+
signals:
152+
/** Emited when the user changes the expression in the widget.
153+
* Users of this widget should connect to this signal to decide if to let the user
154+
* continue.
155+
* @param isVaild Is true if the expression the user has typed is vaild.
156+
*/
157+
void expressionParsed(bool isVaild);
158+
159+
private:
160+
void fillFieldValues(int fieldIndex, int countLimit);
161+
162+
QgsVectorLayer *mLayer;
163+
QStandardItemModel *mModel;
164+
QgsExpressionItemSearhProxy *mProxyModel;
165+
QMap<QString, QgsExpressionItem*> mExpressionGroups;
166+
QgsFeature* mFeature;
167+
};
168+
169+
#endif // QGSEXPRESSIONBUILDER_H

‎src/ui/qgsexpressionbuilder.ui

Lines changed: 495 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>QgsExpressionBuilderDialogBase</class>
4+
<widget class="QDialog" name="QgsExpressionBuilderDialogBase">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>625</width>
10+
<height>554</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>Expression string builder</string>
15+
</property>
16+
<layout class="QGridLayout" name="gridLayout">
17+
<item row="0" column="0">
18+
<widget class="QgsExpressionBuilderWidget" name="builder" native="true">
19+
<property name="autoFillBackground">
20+
<bool>false</bool>
21+
</property>
22+
<property name="styleSheet">
23+
<string notr="true"/>
24+
</property>
25+
</widget>
26+
</item>
27+
<item row="1" column="0">
28+
<widget class="QDialogButtonBox" name="buttonBox">
29+
<property name="orientation">
30+
<enum>Qt::Horizontal</enum>
31+
</property>
32+
<property name="standardButtons">
33+
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
34+
</property>
35+
</widget>
36+
</item>
37+
</layout>
38+
</widget>
39+
<customwidgets>
40+
<customwidget>
41+
<class>QgsExpressionBuilderWidget</class>
42+
<extends>QWidget</extends>
43+
<header>qgsexpressionbuilderwidget.h</header>
44+
<container>1</container>
45+
</customwidget>
46+
</customwidgets>
47+
<resources/>
48+
<connections>
49+
<connection>
50+
<sender>buttonBox</sender>
51+
<signal>accepted()</signal>
52+
<receiver>QgsExpressionBuilderDialogBase</receiver>
53+
<slot>accept()</slot>
54+
<hints>
55+
<hint type="sourcelabel">
56+
<x>227</x>
57+
<y>494</y>
58+
</hint>
59+
<hint type="destinationlabel">
60+
<x>157</x>
61+
<y>274</y>
62+
</hint>
63+
</hints>
64+
</connection>
65+
<connection>
66+
<sender>buttonBox</sender>
67+
<signal>rejected()</signal>
68+
<receiver>QgsExpressionBuilderDialogBase</receiver>
69+
<slot>reject()</slot>
70+
<hints>
71+
<hint type="sourcelabel">
72+
<x>295</x>
73+
<y>500</y>
74+
</hint>
75+
<hint type="destinationlabel">
76+
<x>286</x>
77+
<y>274</y>
78+
</hint>
79+
</hints>
80+
</connection>
81+
</connections>
82+
</ui>

‎src/ui/qgslabelingguibase.ui

Lines changed: 123 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -18,43 +18,6 @@
1818
<normaloff>../../../../.designer/backup</normaloff>../../../../.designer/backup</iconset>
1919
</property>
2020
<layout class="QGridLayout" name="gridLayout_8">
21-
<item row="0" column="0">
22-
<layout class="QHBoxLayout" name="horizontalLayout_3">
23-
<item>
24-
<widget class="QCheckBox" name="chkEnableLabeling">
25-
<property name="text">
26-
<string>Label this layer</string>
27-
</property>
28-
</widget>
29-
</item>
30-
<item>
31-
<spacer name="horizontalSpacer_5">
32-
<property name="orientation">
33-
<enum>Qt::Horizontal</enum>
34-
</property>
35-
<property name="sizeHint" stdset="0">
36-
<size>
37-
<width>40</width>
38-
<height>20</height>
39-
</size>
40-
</property>
41-
</spacer>
42-
</item>
43-
<item>
44-
<widget class="QLabel" name="label_20">
45-
<property name="text">
46-
<string>Field with labels</string>
47-
</property>
48-
<property name="alignment">
49-
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
50-
</property>
51-
</widget>
52-
</item>
53-
<item>
54-
<widget class="QComboBox" name="cboFieldName"/>
55-
</item>
56-
</layout>
57-
</item>
5821
<item row="1" column="0">
5922
<layout class="QHBoxLayout" name="horizontalLayout_4">
6023
<item>
@@ -75,6 +38,9 @@
7538
</item>
7639
<item>
7740
<widget class="QgsLabelPreview" name="lblFontPreview">
41+
<property name="enabled">
42+
<bool>true</bool>
43+
</property>
7844
<property name="sizePolicy">
7945
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
8046
<horstretch>0</horstretch>
@@ -99,6 +65,9 @@
9965
</item>
10066
<item row="2" column="0">
10167
<widget class="QTabWidget" name="mTabWidget">
68+
<property name="enabled">
69+
<bool>false</bool>
70+
</property>
10271
<property name="currentIndex">
10372
<number>0</number>
10473
</property>
@@ -430,6 +399,18 @@
430399
<layout class="QGridLayout" name="gridLayout_6">
431400
<item row="0" column="0">
432401
<widget class="QScrollArea" name="scrollArea_2">
402+
<property name="autoFillBackground">
403+
<bool>false</bool>
404+
</property>
405+
<property name="frameShape">
406+
<enum>QFrame::NoFrame</enum>
407+
</property>
408+
<property name="frameShadow">
409+
<enum>QFrame::Plain</enum>
410+
</property>
411+
<property name="lineWidth">
412+
<number>0</number>
413+
</property>
433414
<property name="widgetResizable">
434415
<bool>true</bool>
435416
</property>
@@ -438,8 +419,8 @@
438419
<rect>
439420
<x>0</x>
440421
<y>0</y>
441-
<width>643</width>
442-
<height>478</height>
422+
<width>647</width>
423+
<height>487</height>
443424
</rect>
444425
</property>
445426
<layout class="QGridLayout" name="gridLayout_13">
@@ -542,7 +523,7 @@
542523
<item>
543524
<widget class="QCheckBox" name="chkNoObstacle">
544525
<property name="enabled">
545-
<bool>true</bool>
526+
<bool>false</bool>
546527
</property>
547528
<property name="text">
548529
<string>Features don't act as obstacles for labels</string>
@@ -565,7 +546,7 @@
565546
<item>
566547
<widget class="QPushButton" name="btnEngineSettings">
567548
<property name="enabled">
568-
<bool>true</bool>
549+
<bool>false</bool>
569550
</property>
570551
<property name="text">
571552
<string>Engine settings</string>
@@ -884,6 +865,9 @@
884865
<layout class="QGridLayout" name="gridLayout_3">
885866
<item row="3" column="1">
886867
<widget class="QScrollArea" name="scrollArea">
868+
<property name="frameShape">
869+
<enum>QFrame::NoFrame</enum>
870+
</property>
887871
<property name="widgetResizable">
888872
<bool>true</bool>
889873
</property>
@@ -892,8 +876,8 @@
892876
<rect>
893877
<x>0</x>
894878
<y>0</y>
895-
<width>643</width>
896-
<height>586</height>
879+
<width>647</width>
880+
<height>615</height>
897881
</rect>
898882
</property>
899883
<layout class="QGridLayout" name="gridLayout_11">
@@ -1098,6 +1082,50 @@
10981082
</property>
10991083
</widget>
11001084
</item>
1085+
<item row="0" column="0">
1086+
<layout class="QHBoxLayout" name="horizontalLayout_3">
1087+
<item>
1088+
<widget class="QCheckBox" name="chkEnableLabeling">
1089+
<property name="text">
1090+
<string>Label this layer with</string>
1091+
</property>
1092+
</widget>
1093+
</item>
1094+
<item>
1095+
<widget class="QComboBox" name="cboFieldName">
1096+
<property name="enabled">
1097+
<bool>false</bool>
1098+
</property>
1099+
<property name="editable">
1100+
<bool>false</bool>
1101+
</property>
1102+
</widget>
1103+
</item>
1104+
<item>
1105+
<widget class="QToolButton" name="btnExpression">
1106+
<property name="enabled">
1107+
<bool>false</bool>
1108+
</property>
1109+
<property name="text">
1110+
<string>...</string>
1111+
</property>
1112+
</widget>
1113+
</item>
1114+
<item>
1115+
<spacer name="horizontalSpacer_5">
1116+
<property name="orientation">
1117+
<enum>Qt::Horizontal</enum>
1118+
</property>
1119+
<property name="sizeHint" stdset="0">
1120+
<size>
1121+
<width>40</width>
1122+
<height>20</height>
1123+
</size>
1124+
</property>
1125+
</spacer>
1126+
</item>
1127+
</layout>
1128+
</item>
11011129
</layout>
11021130
</widget>
11031131
<layoutdefault spacing="6" margin="11"/>
@@ -1127,8 +1155,8 @@
11271155
<slot>accept()</slot>
11281156
<hints>
11291157
<hint type="sourcelabel">
1130-
<x>344</x>
1131-
<y>575</y>
1158+
<x>353</x>
1159+
<y>459</y>
11321160
</hint>
11331161
<hint type="destinationlabel">
11341162
<x>309</x>
@@ -1143,14 +1171,62 @@
11431171
<slot>reject()</slot>
11441172
<hints>
11451173
<hint type="sourcelabel">
1146-
<x>344</x>
1147-
<y>575</y>
1174+
<x>353</x>
1175+
<y>459</y>
11481176
</hint>
11491177
<hint type="destinationlabel">
11501178
<x>353</x>
11511179
<y>430</y>
11521180
</hint>
11531181
</hints>
11541182
</connection>
1183+
<connection>
1184+
<sender>chkEnableLabeling</sender>
1185+
<signal>clicked(bool)</signal>
1186+
<receiver>cboFieldName</receiver>
1187+
<slot>setEnabled(bool)</slot>
1188+
<hints>
1189+
<hint type="sourcelabel">
1190+
<x>127</x>
1191+
<y>25</y>
1192+
</hint>
1193+
<hint type="destinationlabel">
1194+
<x>248</x>
1195+
<y>26</y>
1196+
</hint>
1197+
</hints>
1198+
</connection>
1199+
<connection>
1200+
<sender>chkEnableLabeling</sender>
1201+
<signal>clicked(bool)</signal>
1202+
<receiver>mTabWidget</receiver>
1203+
<slot>setEnabled(bool)</slot>
1204+
<hints>
1205+
<hint type="sourcelabel">
1206+
<x>59</x>
1207+
<y>22</y>
1208+
</hint>
1209+
<hint type="destinationlabel">
1210+
<x>91</x>
1211+
<y>89</y>
1212+
</hint>
1213+
</hints>
1214+
</connection>
1215+
<connection>
1216+
<sender>chkEnableLabeling</sender>
1217+
<signal>clicked(bool)</signal>
1218+
<receiver>btnExpression</receiver>
1219+
<slot>setEnabled(bool)</slot>
1220+
<hints>
1221+
<hint type="sourcelabel">
1222+
<x>70</x>
1223+
<y>28</y>
1224+
</hint>
1225+
<hint type="destinationlabel">
1226+
<x>291</x>
1227+
<y>32</y>
1228+
</hint>
1229+
</hints>
1230+
</connection>
11551231
</connections>
11561232
</ui>

0 commit comments

Comments
 (0)
Please sign in to comment.