35#include "moc_qgsrulebasedlabelingwidget.cpp"
37using namespace Qt::StringLiterals;
73 mCopyAction =
new QAction( tr(
"Copy" ),
this );
74 mCopyAction->setShortcut( QKeySequence( QKeySequence::Copy ) );
75 mPasteAction =
new QAction( tr(
"Paste" ),
this );
76 mPasteAction->setShortcut( QKeySequence( QKeySequence::Paste ) );
77 mDeleteAction =
new QAction( tr(
"Remove Rule" ),
this );
78 mDeleteAction->setShortcut( QKeySequence( QKeySequence::Delete ) );
80 viewRules->addAction( mCopyAction );
81 viewRules->addAction( mPasteAction );
82 viewRules->addAction( mDeleteAction );
84 connect( viewRules, &QAbstractItemView::doubleClicked,
this,
static_cast<void (
QgsRuleBasedLabelingWidget::* )(
const QModelIndex & )
>( &QgsRuleBasedLabelingWidget::editRule ) );
86 connect( btnAddRule, &QAbstractButton::clicked,
this, &QgsRuleBasedLabelingWidget::addRule );
87 connect( btnEditRule, &QAbstractButton::clicked,
this,
static_cast<void (
QgsRuleBasedLabelingWidget::* )()
>( &QgsRuleBasedLabelingWidget::editRule ) );
88 connect( btnRemoveRule, &QAbstractButton::clicked,
this, &QgsRuleBasedLabelingWidget::removeRule );
89 connect( mCopyAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::copy );
90 connect( mPasteAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::paste );
91 connect( mDeleteAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::removeRule );
93 if ( mLayer->labeling() && mLayer->labeling()->type() ==
"rule-based"_L1 )
98 else if ( mLayer->labeling() && mLayer->labeling()->type() ==
"simple"_L1 )
101 mRootRule = std::make_unique<class QgsRuleBasedLabeling::Rule>(
nullptr );
102 auto newSettings = std::make_unique<QgsPalLayerSettings>( mLayer->labeling()->settings() );
103 newSettings->drawLabels =
true;
108 mRootRule = std::make_unique<class QgsRuleBasedLabeling::Rule>(
nullptr );
112 viewRules->setModel( mModel );
128 mCopyAction->setShortcut( QKeySequence() );
130 mPasteAction->setShortcut( QKeySequence() );
132 mDeleteAction->setShortcut( QKeySequence() );
137void QgsRuleBasedLabelingWidget::addRule()
145 const QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
146 mModel->
insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
147 const QModelIndex newindex = mModel->
index( currentIndex.row() + 1, 0, currentIndex.parent() );
148 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
153 const int rows = mModel->rowCount();
154 mModel->insertRule( QModelIndex(), rows, newrule );
155 const QModelIndex newindex = mModel->index( rows, 0 );
156 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
161void QgsRuleBasedLabelingWidget::ruleWidgetPanelAccepted(
QgsPanelWidget *panel )
163 QgsLabelingRulePropsWidget *widget = qobject_cast<QgsLabelingRulePropsWidget *>( panel );
166 const QModelIndex index = viewRules->selectionModel()->currentIndex();
167 mModel->updateRule( index.parent(), index.row() );
170void QgsRuleBasedLabelingWidget::liveUpdateRuleFromPanel()
172 ruleWidgetPanelAccepted( qobject_cast<QgsPanelWidget *>( sender() ) );
176void QgsRuleBasedLabelingWidget::editRule()
178 editRule( viewRules->selectionModel()->currentIndex() );
181void QgsRuleBasedLabelingWidget::editRule(
const QModelIndex &index )
183 if ( !index.isValid() )
186 QgsRuleBasedLabeling::Rule *rule = mModel->ruleForIndex( index );
191 QgsLabelingRulePropsWidget *widget =
new QgsLabelingRulePropsWidget( rule, mLayer,
this, mCanvas );
199 QgsLabelingRulePropsDialog dlg( rule, mLayer,
this, mCanvas );
202 mModel->updateRule( index.parent(), index.row() );
207void QgsRuleBasedLabelingWidget::removeRule()
209 const QItemSelection sel = viewRules->selectionModel()->selection();
210 const auto constSel = sel;
211 for (
const QItemSelectionRange &range : constSel )
213 if ( range.isValid() )
214 mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
217 viewRules->selectionModel()->clear();
220void QgsRuleBasedLabelingWidget::copy()
222 const QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
224 if ( indexlist.isEmpty() )
227 QMimeData *mime = mModel->mimeData( indexlist );
228 QApplication::clipboard()->setMimeData( mime );
231void QgsRuleBasedLabelingWidget::paste()
233 const QMimeData *mime = QApplication::clipboard()->mimeData();
237 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
239 if ( indexlist.isEmpty() )
240 index = mModel->index( mModel->rowCount(), 0 );
242 index = indexlist.first();
243 mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
248 QItemSelectionModel *sel = viewRules->selectionModel();
249 const QModelIndex idx = sel->currentIndex();
250 if ( !idx.isValid() )
252 return mModel->ruleForIndex( idx );
261 setWindowModality( Qt::WindowModal );
264 QVBoxLayout *layout =
new QVBoxLayout(
this );
266 scrollArea->setFrameShape( QFrame::NoFrame );
267 layout->addWidget( scrollArea );
269 buttonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
272 scrollArea->setWidget( mPropsWidget );
273 layout->addWidget( buttonBox );
274 this->setWindowTitle(
"Edit Rule" );
278 connect( buttonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
279 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsLabelingRulePropsDialog::showHelp );
284 mPropsWidget->testFilter();
289 mPropsWidget->buildExpression();
294 mPropsWidget->apply();
298void QgsLabelingRulePropsDialog::showHelp()
300 QgsHelp::openHelp( u
"working_with_vector/vector_properties.html#rule-based-labeling"_s );
306 : QAbstractItemModel(
parent )
312 if ( !
index.isValid() )
313 return Qt::ItemIsDropEnabled;
316 const Qt::ItemFlag drop = (
index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
318 const Qt::ItemFlag checkable = (
index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
320 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | checkable | Qt::ItemIsDragEnabled | drop;
325 if ( !
index.isValid() )
330 if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
332 switch (
index.column() )
355 else if ( role == Qt::DecorationRole &&
index.column() == 0 && rule->
settings() )
360 else if ( role == Qt::TextAlignmentRole )
362 return (
index.column() == 2 ||
index.column() == 3 ) ?
static_cast<Qt::Alignment::Int
>( Qt::AlignRight ) :
static_cast<Qt::Alignment::Int
>( Qt::AlignLeft );
364 else if ( role == Qt::FontRole &&
index.column() == 1 )
369 italicFont.setItalic(
true );
374 else if ( role == Qt::EditRole )
376 switch (
index.column() )
392 else if ( role == Qt::CheckStateRole )
394 if (
index.column() != 0 )
396 return rule->
active() ? Qt::Checked : Qt::Unchecked;
404 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 5 )
407 lst << tr(
"Label" ) << tr(
"Rule" ) << tr(
"Min. Scale" ) << tr(
"Max. Scale" ) << tr(
"Text" );
416 if (
parent.column() > 0 )
421 return parentRule->
children().count();
431 if ( hasIndex( row, column,
parent ) )
435 return createIndex( row, column, childRule );
437 return QModelIndex();
442 if ( !
index.isValid() )
443 return QModelIndex();
449 return QModelIndex();
452 const int row = parentRule->
parent()->
children().indexOf( parentRule );
454 return createIndex( row, 0, parentRule );
459 if ( !
index.isValid() )
464 if ( role == Qt::CheckStateRole )
466 rule->
setActive( value.toInt() == Qt::Checked );
471 if ( role != Qt::EditRole )
474 switch (
index.column() )
503 return Qt::MoveAction;
509 types << u
"application/vnd.text.list"_s;
517 if ( ruleElem.hasAttribute( u
"label"_s ) )
518 ruleElem.setAttribute( u
"description"_s, ruleElem.attribute( u
"label"_s ) );
521 QDomElement childRuleElem = ruleElem.firstChildElement( u
"rule"_s );
522 while ( !childRuleElem.isNull() )
525 childRuleElem = childRuleElem.nextSiblingElement( u
"rule"_s );
531 QMimeData *
mimeData =
new QMimeData();
532 QByteArray encodedData;
534 QDataStream stream( &encodedData, QIODevice::WriteOnly );
536 const auto constIndexes = indexes;
537 for (
const QModelIndex &
index : constIndexes )
540 if ( !
index.isValid() ||
index.column() != 0 )
548 QDomElement rootElem = doc.createElement( u
"rule_mime"_s );
549 rootElem.setAttribute( u
"type"_s, u
"labeling"_s );
551 rootElem.appendChild( rulesElem );
552 doc.appendChild( rootElem );
556 stream << doc.toString( -1 );
559 mimeData->setData( u
"application/vnd.text.list"_s, encodedData );
567 if ( action == Qt::IgnoreAction )
570 if ( !
data->hasFormat( u
"application/vnd.text.list"_s ) )
573 if (
parent.column() > 0 )
576 QByteArray encodedData =
data->data( u
"application/vnd.text.list"_s );
577 QDataStream stream( &encodedData, QIODevice::ReadOnly );
586 while ( !stream.atEnd() )
592 if ( !doc.setContent( text ) )
594 const QDomElement rootElem = doc.documentElement();
595 if ( rootElem.tagName() !=
"rule_mime"_L1 )
597 QDomElement ruleElem = rootElem.firstChildElement( u
"rule"_s );
598 if ( rootElem.attribute( u
"type"_s ) ==
"renderer"_L1 )
613 if ( row < 0 || row >= parentRule->
children().count() )
616 beginRemoveRows(
parent, row, row + count - 1 );
618 for (
int i = 0; i < count; i++ )
620 if ( row < parentRule->children().count() )
626 QgsDebugError( u
"trying to remove invalid index - this should not happen!"_s );
637 if (
index.isValid() )
644 beginInsertRows(
parent, before, before );
663 , mMapCanvas( mapCanvas )
667 QButtonGroup *radioGroup =
new QButtonGroup(
this );
668 radioGroup->addButton( mFilterRadio );
669 radioGroup->addButton( mElseRadio );
671 mElseRadio->setChecked( mRule->isElse() );
672 mFilterRadio->setChecked( !mRule->isElse() );
673 editFilter->setText( mRule->filterExpression() );
674 editFilter->setToolTip( mRule->filterExpression() );
675 editDescription->setText( mRule->description() );
676 editDescription->setToolTip( mRule->description() );
678 if ( mRule->dependsOnScale() )
680 groupScale->setChecked(
true );
682 mScaleRangeWidget->setScaleRange( std::max(
rule->minimumScale(), 0.0 ), std::max(
rule->maximumScale(), 0.0 ) );
684 mScaleRangeWidget->setMapCanvas( mMapCanvas );
686 if ( mRule->settings() )
688 groupSettings->setChecked(
true );
689 mSettings = std::make_unique<QgsPalLayerSettings>( *mRule->settings() );
693 groupSettings->setChecked(
false );
694 mSettings = std::make_unique<QgsPalLayerSettings>();
697 mLabelingGui =
new QgsLabelingGui( mMapCanvas, *mSettings,
this );
698 mLabelingGui->layout()->setContentsMargins( 0, 0, 0, 0 );
699 QVBoxLayout *l =
new QVBoxLayout;
700 l->addWidget( mLabelingGui );
701 groupSettings->setLayout( l );
703 mLabelingGui->setLabelMode( QgsLabelingGui::Labels );
704 mLabelingGui->setLayer( mLayer );
714 connect( mFilterRadio, &QRadioButton::toggled,
this, [
this](
bool toggled ) { filterFrame->setEnabled( toggled ); } );
715 connect( mElseRadio, &QRadioButton::toggled,
this, [
this](
bool toggled ) {
717 editFilter->setText( u
"ELSE"_s );
727 mLabelingGui->setDockMode(
dockMode );
732 if ( !mFilterRadio->isChecked() )
738 QMessageBox::critical(
this, tr(
"Test Filter" ), tr(
"Filter expression parsing error:\n" ) + filter.
parserErrorString() );
744 if ( !filter.
prepare( &context ) )
746 QMessageBox::critical(
this, tr(
"Test Filter" ), filter.
evalErrorString() );
750 QApplication::setOverrideCursor( Qt::WaitCursor );
760 const QVariant value = filter.
evaluate( &context );
761 if ( value.toInt() != 0 )
767 QApplication::restoreOverrideCursor();
769 QMessageBox::information(
this, tr(
"Test Filter" ), tr(
"Filter returned %n feature(s)",
"number of filtered features", count ) );
785 const QString filter = mElseRadio->isChecked() ? u
"ELSE"_s : editFilter->text().trimmed();
786 mRule->setFilterExpression( filter );
787 mRule->setDescription( editDescription->text() );
788 mRule->setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
789 mRule->setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
790 mRule->setSettings( groupSettings->isChecked() ?
new QgsPalLayerSettings( mLabelingGui->layerSettings() ) :
nullptr );
static QgsPalLayerSettings defaultSettingsForLayer(const QgsVectorLayer *layer)
Returns the default layer settings to use for the specified vector layer.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
A generic dialog for building expression strings.
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...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QString evalErrorString() const
Returns evaluation error.
QString parserErrorString() const
Returns parser error.
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QVariant evaluate()
Evaluate the feature and return the result.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
void buildExpression()
Open the expression builder widget.
QgsLabelingRulePropsDialog(QgsRuleBasedLabeling::Rule *rule, QgsVectorLayer *layer, QWidget *parent=nullptr, QgsMapCanvas *mapCanvas=nullptr)
Constructor for QgsLabelingRulePropsDialog.
void testFilter()
Test the filter that is set in the widget.
QgsRuleBasedLabeling::Rule * rule()
Returns the current set rule.
void accept() override
Apply any changes from the widget to the set rule.
Map canvas is a class for displaying all GIS data types on a canvas.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
Base class for all map layer types.
Contains configuration for rendering maps.
Contains settings for how a map layer will be labeled.
QString fieldName
Name of field (or an expression) to use for label text.
static QPixmap labelSettingsPreviewPixmap(const QgsPalLayerSettings &settings, QSize size, const QString &previewText=QString(), int padding=0, const QgsScreenProperties &screen=QgsScreenProperties())
Returns a pixmap preview for label settings.
static QgsProject * instance()
Returns the QgsProject singleton instance.
A container for the context for various read/write operations on 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.
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.
const QgsRuleBasedLabeling::RuleList & children() const
Returns all children rules of this rule.
QString filterExpression() const
A filter that will check if this rule applies.
const QgsRuleBasedLabeling::Rule * parent() const
The parent rule.
bool active() const
Returns if this rule is active.
QgsPalLayerSettings * settings() const
Returns the labeling settings.
void removeChildAt(int i)
delete child rule
void setActive(bool state)
Sets if this rule is active.
static QgsRuleBasedLabeling::Rule * create(const QDomElement &ruleElem, const QgsReadWriteContext &context, bool reuseId=true)
Create a rule from an XML definition.
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) const
store labeling info to XML element
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.
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.
QgsRuleBasedLabeling::Rule * clone(bool resetRuleKey=true) const
clone this rule
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, QgsScaleComboBox::RatioMode mode=QgsScaleComboBox::RatioMode::ForceUnitNumerator)
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 dataset.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
#define QgsDebugError(str)
const double ICON_PADDING_FACTOR