34#include "moc_qgsrulebasedlabelingwidget.cpp"
69 mCopyAction =
new QAction( tr(
"Copy" ),
this );
70 mCopyAction->setShortcut( QKeySequence( QKeySequence::Copy ) );
71 mPasteAction =
new QAction( tr(
"Paste" ),
this );
72 mPasteAction->setShortcut( QKeySequence( QKeySequence::Paste ) );
73 mDeleteAction =
new QAction( tr(
"Remove Rule" ),
this );
74 mDeleteAction->setShortcut( QKeySequence( QKeySequence::Delete ) );
76 viewRules->addAction( mCopyAction );
77 viewRules->addAction( mPasteAction );
78 viewRules->addAction( mDeleteAction );
80 connect( viewRules, &QAbstractItemView::doubleClicked,
this,
static_cast<void (
QgsRuleBasedLabelingWidget::* )(
const QModelIndex & )
>( &QgsRuleBasedLabelingWidget::editRule ) );
82 connect( btnAddRule, &QAbstractButton::clicked,
this, &QgsRuleBasedLabelingWidget::addRule );
83 connect( btnEditRule, &QAbstractButton::clicked,
this,
static_cast<void (
QgsRuleBasedLabelingWidget::* )()
>( &QgsRuleBasedLabelingWidget::editRule ) );
84 connect( btnRemoveRule, &QAbstractButton::clicked,
this, &QgsRuleBasedLabelingWidget::removeRule );
85 connect( mCopyAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::copy );
86 connect( mPasteAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::paste );
87 connect( mDeleteAction, &QAction::triggered,
this, &QgsRuleBasedLabelingWidget::removeRule );
89 if ( mLayer->labeling() && mLayer->labeling()->type() == QLatin1String(
"rule-based" ) )
94 else if ( mLayer->labeling() && mLayer->labeling()->type() == QLatin1String(
"simple" ) )
98 auto newSettings = std::make_unique<QgsPalLayerSettings>( mLayer->labeling()->settings() );
99 newSettings->drawLabels =
true;
108 viewRules->setModel( mModel );
126 mCopyAction->setShortcut( QKeySequence() );
128 mPasteAction->setShortcut( QKeySequence() );
130 mDeleteAction->setShortcut( QKeySequence() );
135void QgsRuleBasedLabelingWidget::addRule()
143 const QModelIndex currentIndex = viewRules->selectionModel()->currentIndex();
144 mModel->
insertRule( currentIndex.parent(), currentIndex.row() + 1, newrule );
145 const QModelIndex newindex = mModel->
index( currentIndex.row() + 1, 0, currentIndex.parent() );
146 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
151 const int rows = mModel->rowCount();
152 mModel->insertRule( QModelIndex(), rows, newrule );
153 const QModelIndex newindex = mModel->index( rows, 0 );
154 viewRules->selectionModel()->setCurrentIndex( newindex, QItemSelectionModel::ClearAndSelect );
159void QgsRuleBasedLabelingWidget::ruleWidgetPanelAccepted(
QgsPanelWidget *panel )
161 QgsLabelingRulePropsWidget *widget = qobject_cast<QgsLabelingRulePropsWidget *>( panel );
164 const QModelIndex index = viewRules->selectionModel()->currentIndex();
165 mModel->updateRule( index.parent(), index.row() );
168void QgsRuleBasedLabelingWidget::liveUpdateRuleFromPanel()
170 ruleWidgetPanelAccepted( qobject_cast<QgsPanelWidget *>( sender() ) );
174void QgsRuleBasedLabelingWidget::editRule()
176 editRule( viewRules->selectionModel()->currentIndex() );
179void QgsRuleBasedLabelingWidget::editRule(
const QModelIndex &index )
181 if ( !index.isValid() )
184 QgsRuleBasedLabeling::Rule *rule = mModel->ruleForIndex( index );
189 QgsLabelingRulePropsWidget *widget =
new QgsLabelingRulePropsWidget( rule, mLayer,
this, mCanvas );
197 QgsLabelingRulePropsDialog dlg( rule, mLayer,
this, mCanvas );
200 mModel->updateRule( index.parent(), index.row() );
205void QgsRuleBasedLabelingWidget::removeRule()
207 const QItemSelection sel = viewRules->selectionModel()->selection();
208 const auto constSel = sel;
209 for (
const QItemSelectionRange &range : constSel )
211 if ( range.isValid() )
212 mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
215 viewRules->selectionModel()->clear();
218void QgsRuleBasedLabelingWidget::copy()
220 const QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
222 if ( indexlist.isEmpty() )
225 QMimeData *mime = mModel->mimeData( indexlist );
226 QApplication::clipboard()->setMimeData( mime );
229void QgsRuleBasedLabelingWidget::paste()
231 const QMimeData *mime = QApplication::clipboard()->mimeData();
235 QModelIndexList indexlist = viewRules->selectionModel()->selectedRows();
237 if ( indexlist.isEmpty() )
238 index = mModel->index( mModel->rowCount(), 0 );
240 index = indexlist.first();
241 mModel->dropMimeData( mime, Qt::CopyAction, index.row(), index.column(), index.parent() );
246 QItemSelectionModel *sel = viewRules->selectionModel();
247 const QModelIndex idx = sel->currentIndex();
248 if ( !idx.isValid() )
250 return mModel->ruleForIndex( idx );
259 setWindowModality( Qt::WindowModal );
262 QVBoxLayout *layout =
new QVBoxLayout(
this );
264 scrollArea->setFrameShape( QFrame::NoFrame );
265 layout->addWidget( scrollArea );
267 buttonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
270 scrollArea->setWidget( mPropsWidget );
271 layout->addWidget( buttonBox );
272 this->setWindowTitle(
"Edit Rule" );
276 connect( buttonBox, &QDialogButtonBox::rejected,
this, &QDialog::reject );
277 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsLabelingRulePropsDialog::showHelp );
282 mPropsWidget->testFilter();
287 mPropsWidget->buildExpression();
292 mPropsWidget->apply();
296void QgsLabelingRulePropsDialog::showHelp()
298 QgsHelp::openHelp( QStringLiteral(
"working_with_vector/vector_properties.html#rule-based-labeling" ) );
304 : QAbstractItemModel(
parent )
311 if ( !
index.isValid() )
312 return Qt::ItemIsDropEnabled;
315 const Qt::ItemFlag drop = (
index.column() == 0 ? Qt::ItemIsDropEnabled : Qt::NoItemFlags );
317 const Qt::ItemFlag checkable = (
index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
319 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | checkable | Qt::ItemIsDragEnabled | drop;
324 if ( !
index.isValid() )
329 if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
331 switch (
index.column() )
354 else if ( role == Qt::DecorationRole &&
index.column() == 0 && rule->
settings() )
359 else if ( role == Qt::TextAlignmentRole )
361 return (
index.column() == 2 ||
index.column() == 3 ) ?
static_cast<Qt::Alignment::Int
>( Qt::AlignRight ) :
static_cast<Qt::Alignment::Int
>( Qt::AlignLeft );
363 else if ( role == Qt::FontRole &&
index.column() == 1 )
368 italicFont.setItalic(
true );
373 else if ( role == Qt::EditRole )
375 switch (
index.column() )
391 else if ( role == Qt::CheckStateRole )
393 if (
index.column() != 0 )
395 return rule->
active() ? Qt::Checked : Qt::Unchecked;
403 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 5 )
406 lst << tr(
"Label" ) << tr(
"Rule" ) << tr(
"Min. Scale" ) << tr(
"Max. Scale" ) << tr(
"Text" );
415 if (
parent.column() > 0 )
420 return parentRule->
children().count();
430 if ( hasIndex( row, column,
parent ) )
434 return createIndex( row, column, childRule );
436 return QModelIndex();
441 if ( !
index.isValid() )
442 return QModelIndex();
448 return QModelIndex();
451 const int row = parentRule->
parent()->
children().indexOf( parentRule );
453 return createIndex( row, 0, parentRule );
458 if ( !
index.isValid() )
463 if ( role == Qt::CheckStateRole )
465 rule->
setActive( value.toInt() == Qt::Checked );
470 if ( role != Qt::EditRole )
473 switch (
index.column() )
502 return Qt::MoveAction;
508 types << QStringLiteral(
"application/vnd.text.list" );
516 if ( ruleElem.hasAttribute( QStringLiteral(
"label" ) ) )
517 ruleElem.setAttribute( QStringLiteral(
"description" ), ruleElem.attribute( QStringLiteral(
"label" ) ) );
520 QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral(
"rule" ) );
521 while ( !childRuleElem.isNull() )
524 childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral(
"rule" ) );
530 QMimeData *
mimeData =
new QMimeData();
531 QByteArray encodedData;
533 QDataStream stream( &encodedData, QIODevice::WriteOnly );
535 const auto constIndexes = indexes;
536 for (
const QModelIndex &
index : constIndexes )
539 if ( !
index.isValid() ||
index.column() != 0 )
547 QDomElement rootElem = doc.createElement( QStringLiteral(
"rule_mime" ) );
548 rootElem.setAttribute( QStringLiteral(
"type" ), QStringLiteral(
"labeling" ) );
550 rootElem.appendChild( rulesElem );
551 doc.appendChild( rootElem );
555 stream << doc.toString( -1 );
558 mimeData->setData( QStringLiteral(
"application/vnd.text.list" ), encodedData );
566 if ( action == Qt::IgnoreAction )
569 if ( !
data->hasFormat( QStringLiteral(
"application/vnd.text.list" ) ) )
572 if (
parent.column() > 0 )
575 QByteArray encodedData =
data->data( QStringLiteral(
"application/vnd.text.list" ) );
576 QDataStream stream( &encodedData, QIODevice::ReadOnly );
585 while ( !stream.atEnd() )
591 if ( !doc.setContent( text ) )
593 const QDomElement rootElem = doc.documentElement();
594 if ( rootElem.tagName() != QLatin1String(
"rule_mime" ) )
596 QDomElement ruleElem = rootElem.firstChildElement( QStringLiteral(
"rule" ) );
597 if ( rootElem.attribute( QStringLiteral(
"type" ) ) == QLatin1String(
"renderer" ) )
612 if ( row < 0 || row >= parentRule->
children().count() )
615 beginRemoveRows(
parent, row, row + count - 1 );
617 for (
int i = 0; i < count; i++ )
619 if ( row < parentRule->children().count() )
625 QgsDebugError( QStringLiteral(
"trying to remove invalid index - this should not happen!" ) );
636 if (
index.isValid() )
643 beginInsertRows(
parent, before, before );
662 , mMapCanvas( mapCanvas )
666 QButtonGroup *radioGroup =
new QButtonGroup(
this );
667 radioGroup->addButton( mFilterRadio );
668 radioGroup->addButton( mElseRadio );
670 mElseRadio->setChecked( mRule->isElse() );
671 mFilterRadio->setChecked( !mRule->isElse() );
672 editFilter->setText( mRule->filterExpression() );
673 editFilter->setToolTip( mRule->filterExpression() );
674 editDescription->setText( mRule->description() );
675 editDescription->setToolTip( mRule->description() );
677 if ( mRule->dependsOnScale() )
679 groupScale->setChecked(
true );
681 mScaleRangeWidget->setScaleRange( std::max(
rule->minimumScale(), 0.0 ), std::max(
rule->maximumScale(), 0.0 ) );
683 mScaleRangeWidget->setMapCanvas( mMapCanvas );
685 if ( mRule->settings() )
687 groupSettings->setChecked(
true );
692 groupSettings->setChecked(
false );
696 mLabelingGui =
new QgsLabelingGui( mMapCanvas, *mSettings,
this );
697 mLabelingGui->layout()->setContentsMargins( 0, 0, 0, 0 );
698 QVBoxLayout *l =
new QVBoxLayout;
699 l->addWidget( mLabelingGui );
700 groupSettings->setLayout( l );
702 mLabelingGui->setLabelMode( QgsLabelingGui::Labels );
703 mLabelingGui->setLayer( mLayer );
713 connect( mFilterRadio, &QRadioButton::toggled,
this, [
this](
bool toggled ) { filterFrame->setEnabled( toggled ); } );
714 connect( mElseRadio, &QRadioButton::toggled,
this, [
this](
bool toggled ) {
if ( toggled ) editFilter->setText( QStringLiteral(
"ELSE" ) ); } );
725 mLabelingGui->setDockMode(
dockMode );
730 if ( !mFilterRadio->isChecked() )
736 QMessageBox::critical(
this, tr(
"Test Filter" ), tr(
"Filter expression parsing error:\n" ) + filter.
parserErrorString() );
742 if ( !filter.
prepare( &context ) )
744 QMessageBox::critical(
this, tr(
"Test Filter" ), filter.
evalErrorString() );
748 QApplication::setOverrideCursor( Qt::WaitCursor );
758 const QVariant value = filter.
evaluate( &context );
759 if ( value.toInt() != 0 )
765 QApplication::restoreOverrideCursor();
767 QMessageBox::information(
this, tr(
"Test Filter" ), tr(
"Filter returned %n feature(s)",
"number of filtered features", count ) );
783 const QString filter = mElseRadio->isChecked() ? QStringLiteral(
"ELSE" ) : editFilter->text().trimmed();
784 mRule->setFilterExpression( filter );
785 mRule->setDescription( editDescription->text() );
786 mRule->setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
787 mRule->setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
788 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