Skip to content

Commit e5cca75

Browse files
committedSep 24, 2015
Rule-based labeling GUI enhancements
- new column with label text - copy / paste / delete rules (also with shortcuts) - no label+description - only description (per rule) - BONUS: copy/paste works also between rule-based renderer and labeling This code has been funded by Tuscany Region (Italy) - SITA (CIG: 63526840AE) and commissioned to Gis3W s.a.s.
1 parent 9970b45 commit e5cca75

7 files changed

+112
-33
lines changed
 

‎src/app/qgsrulebasedlabelingwidget.cpp

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "qgsvectorlayer.h"
88
#include "qgsvectorlayerlabeling.h"
99

10+
#include <QClipboard>
1011
#include <QMessageBox>
1112

1213
QgsRuleBasedLabelingWidget::QgsRuleBasedLabelingWidget( QgsVectorLayer* layer, QgsMapCanvas* canvas, QWidget* parent )
@@ -22,11 +23,25 @@ QgsRuleBasedLabelingWidget::QgsRuleBasedLabelingWidget( QgsVectorLayer* layer, Q
2223
btnEditRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyEdit.png" ) ) );
2324
btnRemoveRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
2425

26+
mCopyAction = new QAction( tr( "Copy" ), this );
27+
mCopyAction->setShortcut( QKeySequence( QKeySequence::Copy ) );
28+
mPasteAction = new QAction( tr( "Paste" ), this );
29+
mPasteAction->setShortcut( QKeySequence( QKeySequence::Paste ) );
30+
mDeleteAction = new QAction( tr( "Remove Rule" ), this );
31+
mDeleteAction->setShortcut( QKeySequence( QKeySequence::Delete ) );
32+
33+
viewRules->addAction( mDeleteAction );
34+
viewRules->addAction( mCopyAction );
35+
viewRules->addAction( mPasteAction );
36+
2537
connect( viewRules, SIGNAL( doubleClicked( const QModelIndex & ) ), this, SLOT( editRule( const QModelIndex & ) ) );
2638

2739
connect( btnAddRule, SIGNAL( clicked() ), this, SLOT( addRule() ) );
2840
connect( btnEditRule, SIGNAL( clicked() ), this, SLOT( editRule() ) );
2941
connect( btnRemoveRule, SIGNAL( clicked() ), this, SLOT( removeRule() ) );
42+
connect( mCopyAction, SIGNAL( triggered( bool ) ), this, SLOT( copy() ) );
43+
connect( mPasteAction, SIGNAL( triggered( bool ) ), this, SLOT( paste() ) );
44+
connect( mDeleteAction, SIGNAL( triggered( bool ) ), this, SLOT( removeRule() ) );
3045

3146
if ( mLayer->labeling() && mLayer->labeling()->type() == "rule-based" )
3247
{
@@ -118,6 +133,30 @@ void QgsRuleBasedLabelingWidget::removeRule()
118133
// TODO mModel->clearFeatureCounts();
119134
}
120135

136+
void QgsRuleBasedLabelingWidget::copy()
137+
{
138+
QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
139+
QgsDebugMsg( QString( "%1" ).arg( indexlist.count() ) );
140+
141+
if ( indexlist.isEmpty() )
142+
return;
143+
144+
QMimeData* mime = mModel->mimeData( indexlist );
145+
QApplication::clipboard()->setMimeData( mime );
146+
}
147+
148+
void QgsRuleBasedLabelingWidget::paste()
149+
{
150+
const QMimeData* mime = QApplication::clipboard()->mimeData();
151+
QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
152+
QModelIndex index;
153+
if ( indexlist.isEmpty() )
154+
index = mModel->index( mModel->rowCount(), 0 );
155+
else
156+
index = indexlist.first();
157+
mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
158+
}
159+
121160
QgsRuleBasedLabeling::Rule* QgsRuleBasedLabelingWidget::currentRule()
122161
{
123162
QItemSelectionModel* sel = viewRules->selectionModel();
@@ -175,7 +214,7 @@ QVariant QgsRuleBasedLabelingModel::data( const QModelIndex& index, int role ) c
175214
{
176215
switch ( index.column() )
177216
{
178-
case 0: return rule->label();
217+
case 0: return rule->description();
179218
case 1:
180219
if ( rule->isElse() )
181220
{
@@ -187,6 +226,7 @@ QVariant QgsRuleBasedLabelingModel::data( const QModelIndex& index, int role ) c
187226
}
188227
case 2: return rule->dependsOnScale() ? _formatScale( rule->scaleMaxDenom() ) : QVariant();
189228
case 3: return rule->dependsOnScale() ? _formatScale( rule->scaleMinDenom() ) : QVariant();
229+
case 4: return rule->settings() ? rule->settings()->fieldName : QVariant();
190230
#if 0 // TODO: feature counts?
191231
case 4:
192232
if ( mFeatureCountMap.count( rule ) == 1 )
@@ -248,10 +288,11 @@ QVariant QgsRuleBasedLabelingModel::data( const QModelIndex& index, int role ) c
248288
{
249289
switch ( index.column() )
250290
{
251-
case 0: return rule->label();
291+
case 0: return rule->description();
252292
case 1: return rule->filterExpression();
253293
case 2: return rule->scaleMaxDenom();
254294
case 3: return rule->scaleMinDenom();
295+
case 4: return rule->settings() ? rule->settings()->fieldName : QVariant();
255296
default: return QVariant();
256297
}
257298
}
@@ -269,7 +310,7 @@ QVariant QgsRuleBasedLabelingModel::headerData( int section, Qt::Orientation ori
269310
{
270311
if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 5 )
271312
{
272-
QStringList lst; lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. scale" ) << tr( "Max. scale" ); // << tr( "Count" ) << tr( "Duplicate count" );
313+
QStringList lst; lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. scale" ) << tr( "Max. scale" ) << tr( "Text" ); // << tr( "Count" ) << tr( "Duplicate count" );
273314
return lst[section];
274315
}
275316
else if ( orientation == Qt::Horizontal && role == Qt::ToolTipRole )
@@ -300,7 +341,7 @@ int QgsRuleBasedLabelingModel::rowCount( const QModelIndex& parent ) const
300341

301342
int QgsRuleBasedLabelingModel::columnCount( const QModelIndex& ) const
302343
{
303-
return 4;
344+
return 5;
304345
}
305346

306347
QModelIndex QgsRuleBasedLabelingModel::index( int row, int column, const QModelIndex& parent ) const
@@ -350,8 +391,8 @@ bool QgsRuleBasedLabelingModel::setData( const QModelIndex& index, const QVarian
350391

351392
switch ( index.column() )
352393
{
353-
case 0: // label
354-
rule->setLabel( value.toString() );
394+
case 0: // description
395+
rule->setDescription( value.toString() );
355396
break;
356397
case 1: // filter
357398
rule->setFilterExpression( value.toString() );
@@ -362,6 +403,11 @@ bool QgsRuleBasedLabelingModel::setData( const QModelIndex& index, const QVarian
362403
case 3: // scale max
363404
rule->setScaleMinDenom( value.toInt() );
364405
break;
406+
case 4: // label text
407+
if ( !rule->settings() )
408+
return false;
409+
rule->settings()->fieldName = value.toString();
410+
break;
365411
default:
366412
return false;
367413
}
@@ -382,6 +428,22 @@ QStringList QgsRuleBasedLabelingModel::mimeTypes() const
382428
return types;
383429
}
384430

431+
// manipulate DOM before dropping it so that rules are more useful
432+
void _renderer2labelingRules( QDomElement& ruleElem )
433+
{
434+
// labeling rules recognize only "description"
435+
if ( ruleElem.hasAttribute( "label" ) )
436+
ruleElem.setAttribute( "description", ruleElem.attribute( "label" ) );
437+
438+
// run recursively
439+
QDomElement childRuleElem = ruleElem.firstChildElement( "rule" );
440+
while ( !childRuleElem.isNull() )
441+
{
442+
_renderer2labelingRules( childRuleElem );
443+
childRuleElem = childRuleElem.nextSiblingElement( "rule" );
444+
}
445+
}
446+
385447
QMimeData*QgsRuleBasedLabelingModel::mimeData( const QModelIndexList& indexes ) const
386448
{
387449
QMimeData *mimeData = new QMimeData();
@@ -401,6 +463,7 @@ QMimeData*QgsRuleBasedLabelingModel::mimeData( const QModelIndexList& indexes )
401463
QDomDocument doc;
402464

403465
QDomElement rootElem = doc.createElement( "rule_mime" );
466+
rootElem.setAttribute( "type", "labeling" ); // for determining whether rules are from renderer or labeling
404467
QDomElement rulesElem = rule->save( doc );
405468
rootElem.appendChild( rulesElem );
406469
doc.appendChild( rootElem );
@@ -449,6 +512,8 @@ bool QgsRuleBasedLabelingModel::dropMimeData( const QMimeData* data, Qt::DropAct
449512
if ( rootElem.tagName() != "rule_mime" )
450513
continue;
451514
QDomElement ruleElem = rootElem.firstChildElement( "rule" );
515+
if ( rootElem.attribute( "type" ) == "renderer" )
516+
_renderer2labelingRules( ruleElem ); // do some modifications so that we load the rules more nicely
452517
QgsRuleBasedLabeling::Rule* rule = QgsRuleBasedLabeling::Rule::create( ruleElem );
453518

454519
insertRule( parent, row + rows, rule );
@@ -526,7 +591,6 @@ QgsLabelingRulePropsDialog::QgsLabelingRulePropsDialog( QgsRuleBasedLabeling::Ru
526591

527592
editFilter->setText( mRule->filterExpression() );
528593
editFilter->setToolTip( mRule->filterExpression() );
529-
editLabel->setText( mRule->label() );
530594
editDescription->setText( mRule->description() );
531595
editDescription->setToolTip( mRule->description() );
532596

@@ -653,7 +717,6 @@ void QgsLabelingRulePropsDialog::buildExpression()
653717
void QgsLabelingRulePropsDialog::accept()
654718
{
655719
mRule->setFilterExpression( editFilter->text() );
656-
mRule->setLabel( editLabel->text() );
657720
mRule->setDescription( editDescription->text() );
658721
// caution: rule uses scale denom, scale widget uses true scales
659722
mRule->setScaleMinDenom( groupScale->isChecked() ? mScaleRangeWidget->minimumScaleDenom() : 0 );

‎src/app/qgsrulebasedlabelingwidget.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ class QgsRuleBasedLabelingWidget : public QWidget, private Ui::QgsRuleBasedLabel
8282
void editRule();
8383
void editRule( const QModelIndex& index );
8484
void removeRule();
85+
void copy();
86+
void paste();
8587

8688
protected:
8789
QgsRuleBasedLabeling::Rule* currentRule();
@@ -92,6 +94,10 @@ class QgsRuleBasedLabelingWidget : public QWidget, private Ui::QgsRuleBasedLabel
9294

9395
QgsRuleBasedLabeling::Rule* mRootRule;
9496
QgsRuleBasedLabelingModel* mModel;
97+
98+
QAction* mCopyAction;
99+
QAction* mPasteAction;
100+
QAction* mDeleteAction;
95101
};
96102

97103

‎src/core/qgsrulebasedlabeling.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ QList<QgsAbstractLabelProvider*> QgsRuleBasedLabelProvider::subProviders()
4242

4343
////////////////////
4444

45-
QgsRuleBasedLabeling::Rule::Rule( QgsPalLayerSettings* settings, int scaleMinDenom, int scaleMaxDenom, const QString& filterExp, const QString& label, const QString& description, bool elseRule )
45+
QgsRuleBasedLabeling::Rule::Rule( QgsPalLayerSettings* settings, int scaleMinDenom, int scaleMaxDenom, const QString& filterExp, const QString& description, bool elseRule )
4646
: mParent( 0 ), mSettings( settings )
4747
, mScaleMinDenom( scaleMinDenom ), mScaleMaxDenom( scaleMaxDenom )
48-
, mFilterExp( filterExp ), mLabel( label ), mDescription( description )
48+
, mFilterExp( filterExp ), mDescription( description )
4949
, mElseRule( elseRule )
5050
, mIsActive( true )
5151
, mFilter( 0 )
@@ -124,7 +124,7 @@ void QgsRuleBasedLabeling::Rule::removeChildAt( int i )
124124
QgsRuleBasedLabeling::Rule*QgsRuleBasedLabeling::Rule::clone() const
125125
{
126126
QgsPalLayerSettings* s = mSettings ? new QgsPalLayerSettings( *mSettings ) : 0;
127-
Rule* newrule = new Rule( s, mScaleMinDenom, mScaleMaxDenom, mFilterExp, mLabel, mDescription );
127+
Rule* newrule = new Rule( s, mScaleMinDenom, mScaleMaxDenom, mFilterExp, mDescription );
128128
newrule->setActive( mIsActive );
129129
// clone children
130130
Q_FOREACH ( Rule* rule, mChildren )
@@ -143,12 +143,11 @@ QgsRuleBasedLabeling::Rule*QgsRuleBasedLabeling::Rule::create( const QDomElement
143143
}
144144

145145
QString filterExp = ruleElem.attribute( "filter" );
146-
QString label = ruleElem.attribute( "label" );
147146
QString description = ruleElem.attribute( "description" );
148147
int scaleMinDenom = ruleElem.attribute( "scalemindenom", "0" ).toInt();
149148
int scaleMaxDenom = ruleElem.attribute( "scalemaxdenom", "0" ).toInt();
150149
//QString ruleKey = ruleElem.attribute( "key" );
151-
Rule* rule = new Rule( settings, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
150+
Rule* rule = new Rule( settings, scaleMinDenom, scaleMaxDenom, filterExp, description );
152151

153152
//if ( !ruleKey.isEmpty() )
154153
// rule->mRuleKey = ruleKey;
@@ -187,8 +186,6 @@ QDomElement QgsRuleBasedLabeling::Rule::save( QDomDocument& doc ) const
187186
ruleElem.setAttribute( "scalemindenom", mScaleMinDenom );
188187
if ( mScaleMaxDenom != 0 )
189188
ruleElem.setAttribute( "scalemaxdenom", mScaleMaxDenom );
190-
if ( !mLabel.isEmpty() )
191-
ruleElem.setAttribute( "label", mLabel );
192189
if ( !mDescription.isEmpty() )
193190
ruleElem.setAttribute( "description", mDescription );
194191
if ( !mIsActive )

‎src/core/qgsrulebasedlabeling.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class CORE_EXPORT QgsRuleBasedLabeling : public QgsAbstractVectorLayerLabeling
2525
{
2626
public:
2727
//! takes ownership of settings
28-
Rule( QgsPalLayerSettings* settings, int scaleMinDenom = 0, int scaleMaxDenom = 0, const QString& filterExp = QString(), const QString& label = QString(), const QString& description = QString(), bool elseRule = false );
28+
Rule( QgsPalLayerSettings* settings, int scaleMinDenom = 0, int scaleMaxDenom = 0, const QString& filterExp = QString(), const QString& description = QString(), bool elseRule = false );
2929
~Rule();
3030

3131
//! The result of registering a rule
@@ -37,7 +37,6 @@ class CORE_EXPORT QgsRuleBasedLabeling : public QgsAbstractVectorLayerLabeling
3737
};
3838

3939
QgsPalLayerSettings* settings() const { return mSettings; }
40-
QString label() const { return mLabel; }
4140
bool dependsOnScale() const { return mScaleMinDenom != 0 || mScaleMaxDenom != 0; }
4241
int scaleMinDenom() const { return mScaleMinDenom; }
4342
int scaleMaxDenom() const { return mScaleMaxDenom; }
@@ -68,7 +67,6 @@ class CORE_EXPORT QgsRuleBasedLabeling : public QgsAbstractVectorLayerLabeling
6867
//! set new settings (or NULL). Deletes old settings if any.
6968
void setSettings( QgsPalLayerSettings* settings );
7069

71-
void setLabel( QString label ) { mLabel = label; }
7270
/**
7371
* Set the minimum denominator for which this rule shall apply.
7472
* E.g. 1000 if it shall be evaluated between 1:1000 and 1:100'000
@@ -198,7 +196,7 @@ class CORE_EXPORT QgsRuleBasedLabeling : public QgsAbstractVectorLayerLabeling
198196
Rule* mParent; // parent rule (NULL only for root rule)
199197
QgsPalLayerSettings* mSettings;
200198
int mScaleMinDenom, mScaleMaxDenom;
201-
QString mFilterExp, mLabel, mDescription;
199+
QString mFilterExp, mDescription;
202200
bool mElseRule;
203201
RuleList mChildren;
204202
RuleList mElseRules;

‎src/gui/symbology-ng/qgsrulebasedrendererv2widget.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,7 @@ QMimeData *QgsRuleBasedRendererV2Model::mimeData( const QModelIndexList &indexes
10031003
QgsSymbolV2Map symbols;
10041004

10051005
QDomElement rootElem = doc.createElement( "rule_mime" );
1006+
rootElem.setAttribute( "type", "renderer" ); // for determining whether rules are from renderer or labeling
10061007
QDomElement rulesElem = rule->save( doc, symbols );
10071008
rootElem.appendChild( rulesElem );
10081009
QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
@@ -1018,6 +1019,24 @@ QMimeData *QgsRuleBasedRendererV2Model::mimeData( const QModelIndexList &indexes
10181019
return mimeData;
10191020
}
10201021

1022+
1023+
// manipulate DOM before dropping it so that rules are more useful
1024+
void _labeling2rendererRules( QDomElement& ruleElem )
1025+
{
1026+
// labeling rules recognize only "description"
1027+
if ( ruleElem.hasAttribute( "description" ) )
1028+
ruleElem.setAttribute( "label", ruleElem.attribute( "description" ) );
1029+
1030+
// run recursively
1031+
QDomElement childRuleElem = ruleElem.firstChildElement( "rule" );
1032+
while ( !childRuleElem.isNull() )
1033+
{
1034+
_labeling2rendererRules( childRuleElem );
1035+
childRuleElem = childRuleElem.nextSiblingElement( "rule" );
1036+
}
1037+
}
1038+
1039+
10211040
bool QgsRuleBasedRendererV2Model::dropMimeData( const QMimeData *data,
10221041
Qt::DropAction action, int row, int column, const QModelIndex &parent )
10231042
{
@@ -1053,11 +1072,15 @@ bool QgsRuleBasedRendererV2Model::dropMimeData( const QMimeData *data,
10531072
QDomElement rootElem = doc.documentElement();
10541073
if ( rootElem.tagName() != "rule_mime" )
10551074
continue;
1075+
if ( rootElem.attribute( "type" ) == "labeling" )
1076+
rootElem.appendChild( doc.createElement( "symbols" ) );
10561077
QDomElement symbolsElem = rootElem.firstChildElement( "symbols" );
10571078
if ( symbolsElem.isNull() )
10581079
continue;
10591080
QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
10601081
QDomElement ruleElem = rootElem.firstChildElement( "rule" );
1082+
if ( rootElem.attribute( "type" ) == "labeling" )
1083+
_labeling2rendererRules( ruleElem );
10611084
QgsRuleBasedRendererV2::Rule* rule = QgsRuleBasedRendererV2::Rule::create( ruleElem, symbolMap );
10621085

10631086
insertRule( parent, row + rows, rule );

‎src/ui/qgslabelingrulepropsdialog.ui

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@
2222
<item row="0" column="0">
2323
<widget class="QLabel" name="label_1">
2424
<property name="text">
25-
<string>Label</string>
25+
<string>Description</string>
2626
</property>
2727
</widget>
2828
</item>
2929
<item row="0" column="1">
30-
<widget class="QLineEdit" name="editLabel"/>
30+
<widget class="QLineEdit" name="editDescription"/>
3131
</item>
3232
<item row="1" column="0">
3333
<widget class="QLabel" name="label_5">
@@ -63,16 +63,6 @@
6363
</item>
6464
</layout>
6565
</item>
66-
<item row="2" column="0">
67-
<widget class="QLabel" name="label_4">
68-
<property name="text">
69-
<string>Description</string>
70-
</property>
71-
</widget>
72-
</item>
73-
<item row="2" column="1">
74-
<widget class="QLineEdit" name="editDescription"/>
75-
</item>
7666
</layout>
7767
</item>
7868
<item>
@@ -136,11 +126,10 @@
136126
</customwidget>
137127
</customwidgets>
138128
<tabstops>
139-
<tabstop>editLabel</tabstop>
129+
<tabstop>editDescription</tabstop>
140130
<tabstop>editFilter</tabstop>
141131
<tabstop>btnExpressionBuilder</tabstop>
142132
<tabstop>btnTestFilter</tabstop>
143-
<tabstop>editDescription</tabstop>
144133
<tabstop>groupScale</tabstop>
145134
<tabstop>groupSettings</tabstop>
146135
<tabstop>buttonBox</tabstop>

‎src/ui/qgsrulebasedlabelingwidget.ui

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
</property>
1717
<item>
1818
<widget class="QTreeView" name="viewRules">
19+
<property name="contextMenuPolicy">
20+
<enum>Qt::ActionsContextMenu</enum>
21+
</property>
1922
<property name="acceptDrops">
2023
<bool>true</bool>
2124
</property>

0 commit comments

Comments
 (0)
Please sign in to comment.