32 #include <QMessageBox>
36 static QList<QgsExpressionContextScope *> _globalProjectAtlasMapLayerScopes(
QgsMapCanvas *mapCanvas,
const QgsMapLayer *layer )
38 QList<QgsExpressionContextScope *> scopes;
68 mCopyAction =
new QAction( tr(
"Copy" ),
this );
69 mCopyAction->setShortcut( QKeySequence( QKeySequence::Copy ) );
70 mPasteAction =
new QAction( tr(
"Paste" ),
this );
71 mPasteAction->setShortcut( QKeySequence( QKeySequence::Paste ) );
72 mDeleteAction =
new QAction( tr(
"Remove Rule" ),
this );
73 mDeleteAction->setShortcut( QKeySequence( QKeySequence::Delete ) );
75 viewRules->addAction( mCopyAction );
76 viewRules->addAction( mPasteAction );
77 viewRules->addAction( mDeleteAction );
79 connect( viewRules, &QAbstractItemView::doubleClicked,
this,
static_cast<void (
QgsRuleBasedLabelingWidget::* )(
const QModelIndex & )
>( &QgsRuleBasedLabelingWidget::editRule ) );
81 connect( btnAddRule, &QAbstractButton::clicked,
this, &QgsRuleBasedLabelingWidget::addRule );
82 connect( btnEditRule, &QAbstractButton::clicked,
this,
static_cast<void (
QgsRuleBasedLabelingWidget::* )()
>( &QgsRuleBasedLabelingWidget::editRule ) );
83 connect( btnRemoveRule, &QAbstractButton::clicked,
this, &QgsRuleBasedLabelingWidget::removeRule );
84 connect( mCopyAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::copy );
85 connect( mPasteAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::paste );
86 connect( mDeleteAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::removeRule );
97 std::unique_ptr< QgsPalLayerSettings > newSettings = std::make_unique< QgsPalLayerSettings >( mLayer->
labeling()->
settings() );
98 newSettings->drawLabels =
true;
107 viewRules->setModel( mModel );
125 mCopyAction->setShortcut( QKeySequence() );
127 mPasteAction->setShortcut( QKeySequence() );
129 mDeleteAction->setShortcut( QKeySequence() );
134 void QgsRuleBasedLabelingWidget::addRule()
142 const QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
143 mModel->
insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
144 const QModelIndex newindex = mModel->
index( currentIndex.row() + 1, 0, currentIndex.parent() );
145 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
150 const int rows = mModel->
rowCount();
151 mModel->
insertRule( QModelIndex(), rows, newrule );
152 const QModelIndex newindex = mModel->
index( rows, 0 );
153 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
158 void QgsRuleBasedLabelingWidget::ruleWidgetPanelAccepted(
QgsPanelWidget *panel )
163 const QModelIndex index = viewRules->selectionModel()->currentIndex();
164 mModel->
updateRule( index.parent(), index.row() );
167 void QgsRuleBasedLabelingWidget::liveUpdateRuleFromPanel()
169 ruleWidgetPanelAccepted( qobject_cast<QgsPanelWidget *>( sender() ) );
173 void QgsRuleBasedLabelingWidget::editRule()
175 editRule( viewRules->selectionModel()->currentIndex() );
178 void QgsRuleBasedLabelingWidget::editRule(
const QModelIndex &index )
180 if ( !index.isValid() )
192 void QgsRuleBasedLabelingWidget::removeRule()
194 const QItemSelection sel = viewRules->selectionModel()->selection();
195 const auto constSel = sel;
196 for (
const QItemSelectionRange &range : constSel )
198 if ( range.isValid() )
199 mModel->
removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
202 viewRules->selectionModel()->clear();
205 void QgsRuleBasedLabelingWidget::copy()
207 const QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
209 if ( indexlist.isEmpty() )
212 QMimeData *mime = mModel->
mimeData( indexlist );
213 QApplication::clipboard()->setMimeData( mime );
216 void QgsRuleBasedLabelingWidget::paste()
218 const QMimeData *mime = QApplication::clipboard()->mimeData();
219 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
221 if ( indexlist.isEmpty() )
224 index = indexlist.first();
225 mModel->
dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
230 QItemSelectionModel *sel = viewRules->selectionModel();
231 const QModelIndex idx = sel->currentIndex();
232 if ( !idx.isValid() )
240 : QAbstractItemModel( parent )
241 , mRootRule( rootRule )
247 if ( !
index.isValid() )
248 return Qt::ItemIsDropEnabled;
251 const Qt::ItemFlag drop = (
index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
253 const Qt::ItemFlag checkable = (
index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
255 return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
256 Qt::ItemIsEditable | checkable |
257 Qt::ItemIsDragEnabled | drop;
262 if ( !
index.isValid() )
267 if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
269 switch (
index.column() )
292 else if ( role == Qt::DecorationRole &&
index.column() == 0 && rule->
settings() )
297 else if ( role == Qt::TextAlignmentRole )
299 return (
index.column() == 2 ||
index.column() == 3 ) ?
static_cast<Qt::Alignment::Int
>( Qt::AlignRight ) :
static_cast<Qt::Alignment::Int
>( Qt::AlignLeft );
301 else if ( role == Qt::FontRole &&
index.column() == 1 )
306 italicFont.setItalic(
true );
311 else if ( role == Qt::EditRole )
313 switch (
index.column() )
329 else if ( role == Qt::CheckStateRole )
331 if (
index.column() != 0 )
333 return rule->
active() ? Qt::Checked : Qt::Unchecked;
341 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 5 )
344 lst << tr(
"Label" ) << tr(
"Rule" ) << tr(
"Min. Scale" ) << tr(
"Max. Scale" ) << tr(
"Text" );
353 if (
parent.column() > 0 )
358 return parentRule->
children().count();
368 if ( hasIndex( row, column,
parent ) )
372 return createIndex( row, column, childRule );
374 return QModelIndex();
379 if ( !
index.isValid() )
380 return QModelIndex();
386 return QModelIndex();
389 const int row = parentRule->
parent()->
children().indexOf( parentRule );
391 return createIndex( row, 0, parentRule );
396 if ( !
index.isValid() )
401 if ( role == Qt::CheckStateRole )
403 rule->
setActive( value.toInt() == Qt::Checked );
408 if ( role != Qt::EditRole )
411 switch (
index.column() )
440 return Qt::MoveAction;
446 types << QStringLiteral(
"application/vnd.text.list" );
454 if ( ruleElem.hasAttribute( QStringLiteral(
"label" ) ) )
455 ruleElem.setAttribute( QStringLiteral(
"description" ), ruleElem.attribute( QStringLiteral(
"label" ) ) );
458 QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral(
"rule" ) );
459 while ( !childRuleElem.isNull() )
462 childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral(
"rule" ) );
468 QMimeData *
mimeData =
new QMimeData();
469 QByteArray encodedData;
471 QDataStream stream( &encodedData, QIODevice::WriteOnly );
473 const auto constIndexes = indexes;
474 for (
const QModelIndex &
index : constIndexes )
477 if ( !
index.isValid() ||
index.column() != 0 )
485 QDomElement rootElem = doc.createElement( QStringLiteral(
"rule_mime" ) );
486 rootElem.setAttribute( QStringLiteral(
"type" ), QStringLiteral(
"labeling" ) );
488 rootElem.appendChild( rulesElem );
489 doc.appendChild( rootElem );
493 stream << doc.toString( -1 );
496 mimeData->setData( QStringLiteral(
"application/vnd.text.list" ), encodedData );
504 if ( action == Qt::IgnoreAction )
507 if ( !
data->hasFormat( QStringLiteral(
"application/vnd.text.list" ) ) )
510 if (
parent.column() > 0 )
513 QByteArray encodedData =
data->data( QStringLiteral(
"application/vnd.text.list" ) );
514 QDataStream stream( &encodedData, QIODevice::ReadOnly );
523 while ( !stream.atEnd() )
529 if ( !doc.setContent( text ) )
531 const QDomElement rootElem = doc.documentElement();
532 if ( rootElem.tagName() != QLatin1String(
"rule_mime" ) )
534 QDomElement ruleElem = rootElem.firstChildElement( QStringLiteral(
"rule" ) );
535 if ( rootElem.attribute( QStringLiteral(
"type" ) ) == QLatin1String(
"renderer" ) )
550 if ( row < 0 || row >= parentRule->
children().count() )
553 beginRemoveRows(
parent, row, row + count - 1 );
555 for (
int i = 0; i < count; i++ )
557 if ( row < parentRule->children().count() )
563 QgsDebugMsg( QStringLiteral(
"trying to remove invalid index - this should not happen!" ) );
574 if (
index.isValid() )
581 beginInsertRows(
parent, before, before );
601 , mSettings( nullptr )
602 , mMapCanvas( mapCanvas )
606 QButtonGroup *radioGroup =
new QButtonGroup(
this );
607 radioGroup->addButton( mFilterRadio );
608 radioGroup->addButton( mElseRadio );
610 mElseRadio->setChecked( mRule->
isElse() );
611 mFilterRadio->setChecked( !mRule->
isElse() );
615 editDescription->setToolTip( mRule->
description() );
619 groupScale->setChecked(
true );
624 mScaleRangeWidget->setMapCanvas( mMapCanvas );
628 groupSettings->setChecked(
true );
633 groupSettings->setChecked(
false );
637 mLabelingGui =
new QgsLabelingGui(
nullptr, mMapCanvas, *mSettings,
this );
638 mLabelingGui->layout()->setContentsMargins( 0, 0, 0, 0 );
639 QVBoxLayout *l =
new QVBoxLayout;
640 l->addWidget( mLabelingGui );
641 groupSettings->setLayout( l );
643 mLabelingGui->setLabelMode( QgsLabelingGui::Labels );
644 mLabelingGui->setLayer( mLayer );
646 connect( btnExpressionBuilder, &QAbstractButton::clicked,
this, &QgsLabelingRulePropsWidget::buildExpression );
647 connect( btnTestFilter, &QAbstractButton::clicked,
this, &QgsLabelingRulePropsWidget::testFilter );
654 connect( mFilterRadio, &QRadioButton::toggled,
this, [ = ](
bool toggled ) { filterFrame->setEnabled( toggled ) ; } );
655 connect( mElseRadio, &QRadioButton::toggled,
this, [ = ](
bool toggled ) {
if ( toggled ) editFilter->setText( QStringLiteral(
"ELSE" ) );} );
666 mLabelingGui->setDockMode(
dockMode );
669 void QgsLabelingRulePropsWidget::testFilter()
671 if ( !mFilterRadio->isChecked() )
675 if ( filter.hasParserError() )
677 QMessageBox::critical(
this, tr(
"Test Filter" ), tr(
"Filter expression parsing error:\n" ) + filter.parserErrorString() );
683 if ( !filter.prepare( &context ) )
685 QMessageBox::critical(
this, tr(
"Test Filter" ), filter.evalErrorString() );
689 QApplication::setOverrideCursor( Qt::WaitCursor );
697 context.setFeature( f );
699 const QVariant value = filter.evaluate( &context );
700 if ( value.toInt() != 0 )
702 if ( filter.hasEvalError() )
706 QApplication::restoreOverrideCursor();
708 QMessageBox::information(
this, tr(
"Test Filter" ), tr(
"Filter returned %n feature(s)",
"number of filtered features", count ) );
712 void QgsLabelingRulePropsWidget::buildExpression()
719 editFilter->setText( dlg.expressionText() );
724 const QString filter = mElseRadio->isChecked() ? QStringLiteral(
"ELSE" ) : editFilter->text();
727 mRule->
setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
728 mRule->
setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
virtual QgsPalLayerSettings settings(const QString &providerId=QString()) const =0
Gets associated label settings.
static QgsPalLayerSettings defaultSettingsForLayer(const QgsVectorLayer *layer)
Returns the default layer settings to use for the specified vector layer.
virtual QString type() const =0
Unique type string of the labeling configuration implementation.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
A generic dialog for building expression strings.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * atlasScope(const QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Map canvas is a class for displaying all GIS data types on a canvas.
QgsExpressionContextScope & expressionContextScope()
Returns a reference to the expression context scope for the map canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
Base class for all map layer types.
The QgsMapSettings class contains configuration for rendering of the map.
Contains settings for how a map layer will be labeled.
static QPixmap labelSettingsPreviewPixmap(const QgsPalLayerSettings &settings, QSize size, const QString &previewText=QString(), int padding=0)
Returns a pixmap preview for label settings.
QString fieldName
Name of field (or an expression) to use for label text.
static QgsProject * instance()
Returns the QgsProject singleton instance.
The class is used as a container of context for various read/write operations on other objects.
Model for rule based rendering rules view.
QgsRuleBasedLabelingModel(QgsRuleBasedLabeling::Rule *rootRule, QObject *parent=nullptr)
constructor
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
QStringList mimeTypes() const override
QgsRuleBasedLabeling::Rule * mRootRule
Qt::DropActions supportedDropActions() const override
void insertRule(const QModelIndex &parent, int before, QgsRuleBasedLabeling::Rule *newrule)
Inserts a new rule at the specified position.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
provide model index for parent's child item
QgsRuleBasedLabeling::Rule * ruleForIndex(const QModelIndex &index) const
Returns the rule at the specified index.
void updateRule(const QModelIndex &parent, int row)
Updates the rule at the specified position.
QMimeData * mimeData(const QModelIndexList &indexes) const override
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QModelIndex parent(const QModelIndex &index) const override
provide parent model index
Qt::ItemFlags flags(const QModelIndex &index) const override
int columnCount(const QModelIndex &=QModelIndex()) const override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
A child rule for QgsRuleBasedLabeling.
const QgsRuleBasedLabeling::RuleList & children() const
Returns all children rules of this rule.
QgsRuleBasedLabeling::Rule * clone() const
clone this rule, return new instance
void setMaximumScale(double scale)
Sets the maximum map scale (i.e.
double maximumScale() const
Returns the maximum map scale (i.e.
void setDescription(const QString &description)
Set a human readable description for this rule.
bool dependsOnScale() const
Determines if scale based labeling is active.
QgsPalLayerSettings * settings() const
Returns the labeling settings.
QString filterExpression() const
A filter that will check if this rule applies.
bool active() const
Returns if this rule is active.
static QgsRuleBasedLabeling::Rule * create(const QDomElement &ruleElem, const QgsReadWriteContext &context)
Create a rule from an XML definition.
void removeChildAt(int i)
delete child rule
void setActive(bool state)
Sets if this rule is active.
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) const
store labeling info to XML element
void setSettings(QgsPalLayerSettings *settings)
Sets new settings (or nullptr). Deletes old settings if any.
bool isElse() const
Check if this rule is an ELSE rule.
void insertChild(int i, QgsRuleBasedLabeling::Rule *rule)
add child rule, take ownership, sets this as parent
void setMinimumScale(double scale)
Sets the minimum map scale (i.e.
const QgsRuleBasedLabeling::Rule * parent() const
The parent rule.
void setFilterExpression(const QString &filterExp)
Set the expression used to check if a given feature shall be rendered with this rule.
double minimumScale() const
Returns the minimum map scale (i.e.
void appendChild(QgsRuleBasedLabeling::Rule *rule)
add child rule, take ownership, sets this as parent
QString description() const
A human readable description for this rule.
Rule based labeling for a vector layer.
QgsRuleBasedLabeling::Rule * rootRule()
static QString toString(double scale)
Helper function to convert a scale double to scale string.
void widgetChanged()
Emitted when the text format defined by the widget changes.
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
const QgsAbstractVectorLayerLabeling * labeling() const
Access to const labeling configuration.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...