35#include "moc_qgsrulebasedlabelingwidget.cpp"
37using namespace Qt::StringLiterals;
72 mCopyAction =
new QAction( tr(
"Copy" ),
this );
73 mCopyAction->setShortcut( QKeySequence( QKeySequence::Copy ) );
74 mPasteAction =
new QAction( tr(
"Paste" ),
this );
75 mPasteAction->setShortcut( QKeySequence( QKeySequence::Paste ) );
76 mDeleteAction =
new QAction( tr(
"Remove Rule" ),
this );
77 mDeleteAction->setShortcut( QKeySequence( QKeySequence::Delete ) );
79 viewRules->addAction( mCopyAction );
80 viewRules->addAction( mPasteAction );
81 viewRules->addAction( mDeleteAction );
83 connect( viewRules, &QAbstractItemView::doubleClicked,
this,
static_cast<void (
QgsRuleBasedLabelingWidget::* )(
const QModelIndex & )
>( &QgsRuleBasedLabelingWidget::editRule ) );
85 connect( btnAddRule, &QAbstractButton::clicked,
this, &QgsRuleBasedLabelingWidget::addRule );
86 connect( btnEditRule, &QAbstractButton::clicked,
this,
static_cast<void (
QgsRuleBasedLabelingWidget::* )()
>( &QgsRuleBasedLabelingWidget::editRule ) );
87 connect( btnRemoveRule, &QAbstractButton::clicked,
this, &QgsRuleBasedLabelingWidget::removeRule );
88 connect( mCopyAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::copy );
89 connect( mPasteAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::paste );
90 connect( mDeleteAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::removeRule );
92 if ( mLayer->labeling() && mLayer->labeling()->type() ==
"rule-based"_L1 )
97 else if ( mLayer->labeling() && mLayer->labeling()->type() ==
"simple"_L1 )
101 auto newSettings = std::make_unique<QgsPalLayerSettings>( mLayer->labeling()->settings() );
102 newSettings->drawLabels =
true;
111 viewRules->setModel( mModel );
129 mCopyAction->setShortcut( QKeySequence() );
131 mPasteAction->setShortcut( QKeySequence() );
133 mDeleteAction->setShortcut( QKeySequence() );
138void QgsRuleBasedLabelingWidget::addRule()
146 const QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
147 mModel->
insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
148 const QModelIndex newindex = mModel->
index( currentIndex.row() + 1, 0, currentIndex.parent() );
149 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
154 const int rows = mModel->rowCount();
155 mModel->insertRule( QModelIndex(), rows, newrule );
156 const QModelIndex newindex = mModel->index( rows, 0 );
157 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
162void QgsRuleBasedLabelingWidget::ruleWidgetPanelAccepted(
QgsPanelWidget *panel )
164 QgsLabelingRulePropsWidget *widget = qobject_cast<QgsLabelingRulePropsWidget *>( panel );
167 const QModelIndex index = viewRules->selectionModel()->currentIndex();
168 mModel->updateRule( index.parent(), index.row() );
171void QgsRuleBasedLabelingWidget::liveUpdateRuleFromPanel()
173 ruleWidgetPanelAccepted( qobject_cast<QgsPanelWidget *>( sender() ) );
177void QgsRuleBasedLabelingWidget::editRule()
179 editRule( viewRules->selectionModel()->currentIndex() );
182void QgsRuleBasedLabelingWidget::editRule(
const QModelIndex &index )
184 if ( !index.isValid() )
187 QgsRuleBasedLabeling::Rule *rule = mModel->ruleForIndex( index );
192 QgsLabelingRulePropsWidget *widget =
new QgsLabelingRulePropsWidget( rule, mLayer,
this, mCanvas );
200 QgsLabelingRulePropsDialog dlg( rule, mLayer,
this, mCanvas );
203 mModel->updateRule( index.parent(), index.row() );
208void QgsRuleBasedLabelingWidget::removeRule()
210 const QItemSelection sel = viewRules->selectionModel()->selection();
211 const auto constSel = sel;
212 for (
const QItemSelectionRange &range : constSel )
214 if ( range.isValid() )
215 mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
218 viewRules->selectionModel()->clear();
221void QgsRuleBasedLabelingWidget::copy()
223 const QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
225 if ( indexlist.isEmpty() )
228 QMimeData *mime = mModel->mimeData( indexlist );
229 QApplication::clipboard()->setMimeData( mime );
232void QgsRuleBasedLabelingWidget::paste()
234 const QMimeData *mime = QApplication::clipboard()->mimeData();
238 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
240 if ( indexlist.isEmpty() )
241 index = mModel->index( mModel->rowCount(), 0 );
243 index = indexlist.first();
244 mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
249 QItemSelectionModel *sel = viewRules->selectionModel();
250 const QModelIndex idx = sel->currentIndex();
251 if ( !idx.isValid() )
253 return mModel->ruleForIndex( idx );
262 setWindowModality( Qt::WindowModal );
265 QVBoxLayout *layout =
new QVBoxLayout(
this );
267 scrollArea->setFrameShape( QFrame::NoFrame );
268 layout->addWidget( scrollArea );
270 buttonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
273 scrollArea->setWidget( mPropsWidget );
274 layout->addWidget( buttonBox );
275 this->setWindowTitle(
"Edit Rule" );
279 connect( buttonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
280 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsLabelingRulePropsDialog::showHelp );
285 mPropsWidget->testFilter();
290 mPropsWidget->buildExpression();
295 mPropsWidget->apply();
299void QgsLabelingRulePropsDialog::showHelp()
301 QgsHelp::openHelp( u
"working_with_vector/vector_properties.html#rule-based-labeling"_s );
307 : QAbstractItemModel(
parent )
314 if ( !
index.isValid() )
315 return Qt::ItemIsDropEnabled;
318 const Qt::ItemFlag drop = (
index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
320 const Qt::ItemFlag checkable = (
index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
322 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | checkable | Qt::ItemIsDragEnabled | drop;
327 if ( !
index.isValid() )
332 if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
334 switch (
index.column() )
357 else if ( role == Qt::DecorationRole &&
index.column() == 0 && rule->
settings() )
362 else if ( role == Qt::TextAlignmentRole )
364 return (
index.column() == 2 ||
index.column() == 3 ) ?
static_cast<Qt::Alignment::Int
>( Qt::AlignRight ) :
static_cast<Qt::Alignment::Int
>( Qt::AlignLeft );
366 else if ( role == Qt::FontRole &&
index.column() == 1 )
371 italicFont.setItalic(
true );
376 else if ( role == Qt::EditRole )
378 switch (
index.column() )
394 else if ( role == Qt::CheckStateRole )
396 if (
index.column() != 0 )
398 return rule->
active() ? Qt::Checked : Qt::Unchecked;
406 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 5 )
409 lst << tr(
"Label" ) << tr(
"Rule" ) << tr(
"Min. Scale" ) << tr(
"Max. Scale" ) << tr(
"Text" );
418 if (
parent.column() > 0 )
423 return parentRule->
children().count();
433 if ( hasIndex( row, column,
parent ) )
437 return createIndex( row, column, childRule );
439 return QModelIndex();
444 if ( !
index.isValid() )
445 return QModelIndex();
451 return QModelIndex();
454 const int row = parentRule->
parent()->
children().indexOf( parentRule );
456 return createIndex( row, 0, parentRule );
461 if ( !
index.isValid() )
466 if ( role == Qt::CheckStateRole )
468 rule->
setActive( value.toInt() == Qt::Checked );
473 if ( role != Qt::EditRole )
476 switch (
index.column() )
505 return Qt::MoveAction;
511 types << u
"application/vnd.text.list"_s;
519 if ( ruleElem.hasAttribute( u
"label"_s ) )
520 ruleElem.setAttribute( u
"description"_s, ruleElem.attribute( u
"label"_s ) );
523 QDomElement childRuleElem = ruleElem.firstChildElement( u
"rule"_s );
524 while ( !childRuleElem.isNull() )
527 childRuleElem = childRuleElem.nextSiblingElement( u
"rule"_s );
533 QMimeData *
mimeData =
new QMimeData();
534 QByteArray encodedData;
536 QDataStream stream( &encodedData, QIODevice::WriteOnly );
538 const auto constIndexes = indexes;
539 for (
const QModelIndex &
index : constIndexes )
542 if ( !
index.isValid() ||
index.column() != 0 )
550 QDomElement rootElem = doc.createElement( u
"rule_mime"_s );
551 rootElem.setAttribute( u
"type"_s, u
"labeling"_s );
553 rootElem.appendChild( rulesElem );
554 doc.appendChild( rootElem );
558 stream << doc.toString( -1 );
561 mimeData->setData( u
"application/vnd.text.list"_s, encodedData );
569 if ( action == Qt::IgnoreAction )
572 if ( !
data->hasFormat( u
"application/vnd.text.list"_s ) )
575 if (
parent.column() > 0 )
578 QByteArray encodedData =
data->data( u
"application/vnd.text.list"_s );
579 QDataStream stream( &encodedData, QIODevice::ReadOnly );
588 while ( !stream.atEnd() )
594 if ( !doc.setContent( text ) )
596 const QDomElement rootElem = doc.documentElement();
597 if ( rootElem.tagName() !=
"rule_mime"_L1 )
599 QDomElement ruleElem = rootElem.firstChildElement( u
"rule"_s );
600 if ( rootElem.attribute( u
"type"_s ) ==
"renderer"_L1 )
615 if ( row < 0 || row >= parentRule->
children().count() )
618 beginRemoveRows(
parent, row, row + count - 1 );
620 for (
int i = 0; i < count; i++ )
622 if ( row < parentRule->children().count() )
628 QgsDebugError( u
"trying to remove invalid index - this should not happen!"_s );
639 if (
index.isValid() )
646 beginInsertRows(
parent, before, before );
665 , mMapCanvas( mapCanvas )
669 QButtonGroup *radioGroup =
new QButtonGroup(
this );
670 radioGroup->addButton( mFilterRadio );
671 radioGroup->addButton( mElseRadio );
673 mElseRadio->setChecked( mRule->isElse() );
674 mFilterRadio->setChecked( !mRule->isElse() );
675 editFilter->setText( mRule->filterExpression() );
676 editFilter->setToolTip( mRule->filterExpression() );
677 editDescription->setText( mRule->description() );
678 editDescription->setToolTip( mRule->description() );
680 if ( mRule->dependsOnScale() )
682 groupScale->setChecked(
true );
684 mScaleRangeWidget->setScaleRange( std::max(
rule->minimumScale(), 0.0 ), std::max(
rule->maximumScale(), 0.0 ) );
686 mScaleRangeWidget->setMapCanvas( mMapCanvas );
688 if ( mRule->settings() )
690 groupSettings->setChecked(
true );
695 groupSettings->setChecked(
false );
699 mLabelingGui =
new QgsLabelingGui( mMapCanvas, *mSettings,
this );
700 mLabelingGui->layout()->setContentsMargins( 0, 0, 0, 0 );
701 QVBoxLayout *l =
new QVBoxLayout;
702 l->addWidget( mLabelingGui );
703 groupSettings->setLayout( l );
705 mLabelingGui->setLabelMode( QgsLabelingGui::Labels );
706 mLabelingGui->setLayer( mLayer );
716 connect( mFilterRadio, &QRadioButton::toggled,
this, [
this](
bool toggled ) { filterFrame->setEnabled( toggled ); } );
717 connect( mElseRadio, &QRadioButton::toggled,
this, [
this](
bool toggled ) {
if ( toggled ) editFilter->setText( u
"ELSE"_s ); } );
728 mLabelingGui->setDockMode(
dockMode );
733 if ( !mFilterRadio->isChecked() )
739 QMessageBox::critical(
this, tr(
"Test Filter" ), tr(
"Filter expression parsing error:\n" ) + filter.
parserErrorString() );
745 if ( !filter.
prepare( &context ) )
747 QMessageBox::critical(
this, tr(
"Test Filter" ), filter.
evalErrorString() );
751 QApplication::setOverrideCursor( Qt::WaitCursor );
761 const QVariant value = filter.
evaluate( &context );
762 if ( value.toInt() != 0 )
768 QApplication::restoreOverrideCursor();
770 QMessageBox::information(
this, tr(
"Test Filter" ), tr(
"Filter returned %n feature(s)",
"number of filtered features", count ) );
786 const QString filter = mElseRadio->isChecked() ? u
"ELSE"_s : editFilter->text().trimmed();
787 mRule->setFilterExpression( filter );
788 mRule->setDescription( editDescription->text() );
789 mRule->setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
790 mRule->setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
791 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