35#include <QRegularExpression>
39 : QToolButton( parent )
40 , mVectorLayer( layer )
43 setFocusPolicy( Qt::StrongFocus );
48 setFixedSize( 2 *
static_cast< int >( 1.25 * iconSize / 2.0 ), 2 *
static_cast< int >( iconSize * 1.1 / 2.0 ) );
50 setIconSize( QSize( iconSize, iconSize ) );
51 setPopupMode( QToolButton::InstantPopup );
55 mDefineMenu =
new QMenu(
this );
56 connect( mDefineMenu, &QMenu::aboutToShow,
this, &QgsPropertyOverrideButton::aboutToShowMenu );
57 connect( mDefineMenu, &QMenu::triggered,
this, &QgsPropertyOverrideButton::menuActionTriggered );
58 setMenu( mDefineMenu );
60 mFieldsMenu =
new QMenu(
this );
61 mActionDataTypes =
new QAction(
this );
63 mActionDataTypes->setMenu( mFieldsMenu );
65 mActionVariables =
new QAction( tr(
"Variable" ),
this );
66 mVariablesMenu =
new QMenu(
this );
67 mActionVariables->setMenu( mVariablesMenu );
69 mActionColors =
new QAction( tr(
"Color" ),
this );
70 mColorsMenu =
new QMenu(
this );
71 mActionColors->setMenu( mColorsMenu );
73 mActionActive =
new QAction(
this );
74 QFont f = mActionActive->font();
76 mActionActive->setFont( f );
78 mActionDescription =
new QAction( tr(
"Description…" ),
this );
80 mActionCreateAuxiliaryField =
new QAction( tr(
"Store Data in the Project" ),
this );
81 mActionCreateAuxiliaryField->setCheckable(
true );
83 mActionExpDialog =
new QAction( tr(
"Edit…" ),
this );
84 mActionExpression =
nullptr;
85 mActionPasteExpr =
new QAction( tr(
"Paste" ),
this );
86 mActionCopyExpr =
new QAction( tr(
"Copy" ),
this );
87 mActionClearExpr =
new QAction( tr(
"Clear" ),
this );
88 mActionAssistant =
new QAction( tr(
"Assistant…" ),
this );
89 QFont assistantFont = mActionAssistant->font();
90 assistantFont.setBold(
true );
91 mActionAssistant->setFont( assistantFont );
92 mDefineMenu->addAction( mActionAssistant );
103 mVectorLayer = layer;
104 mAuxiliaryStorageEnabled = auxiliaryStorageEnabled;
108 mDefinition = definition;
109 mDataTypes = mDefinition.
dataType();
111 mInputDescription = mDefinition.
helpText();
112 mFullDescription.clear();
116 mDataTypesString.clear();
119 switch ( mDataTypes )
122 ts << tr(
"boolean" );
127 ts << tr(
"double" );
131 ts << tr(
"string" );
137 mDataTypesString = ts.join( QLatin1String(
", " ) );
138 mActionDataTypes->setText( tr(
"Field type: " ) + mDataTypesString );
154 mFieldNameList.clear();
155 mFieldDisplayNameList.clear();
165 bool fieldMatch =
false;
166 switch ( mDataTypes )
173 fieldMatch = f.isNumeric() || f.type() == QVariant::String;
177 fieldMatch = f.type() == QVariant::String;
183 mFieldNameList << f.name();
184 mFieldDisplayNameList << f.displayNameWithAlias();
199 mVectorLayer = layer;
206 const auto constMSiblingWidgets = mSiblingWidgets;
207 for (
const SiblingWidget &sw : constMSiblingWidgets )
209 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingCheckState )
212 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingCheckState, natural ) );
218 const auto constMSiblingWidgets = mSiblingWidgets;
219 for (
const SiblingWidget &sw : constMSiblingWidgets )
221 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingEnableState )
224 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingEnableState, natural ) );
230 const auto constMSiblingWidgets = mSiblingWidgets;
231 for (
const SiblingWidget &sw : constMSiblingWidgets )
233 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingVisibility )
236 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingVisibility, natural ) );
242 const auto constMSiblingWidgets = mSiblingWidgets;
243 for (
const SiblingWidget &sw : constMSiblingWidgets )
245 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingExpressionText )
248 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingExpressionText ) );
256 if ( ( event->modifiers() & ( Qt::ControlModifier ) )
257 || event->button() == Qt::RightButton )
259 setActivePrivate( !mProperty.
isActive() );
267 QToolButton::mousePressEvent( event );
281 mFieldName =
property.field();
286 mExpressionString =
property.expressionString();
294 mExpressionString.clear();
296 mProperty = property;
303void QgsPropertyOverrideButton::aboutToShowMenu()
305 mDefineMenu->clear();
309 bool hasExp = !mExpressionString.isEmpty();
310 QString ddTitle = tr(
"Data defined override" );
312 QAction *ddTitleAct = mDefineMenu->addAction( ddTitle );
313 QFont titlefont = ddTitleAct->font();
314 titlefont.setItalic(
true );
315 ddTitleAct->setFont( titlefont );
316 ddTitleAct->setEnabled(
false );
318 bool addActiveAction =
false;
323 addActiveAction = !exp.hasParserError();
328 addActiveAction = mFieldNameList.contains( mFieldName );
331 if ( addActiveAction )
334 mDefineMenu->addAction( mActionActive );
335 mActionActive->setText( mProperty.
isActive() ? tr(
"Deactivate" ) : tr(
"Activate" ) );
336 mActionActive->setData( QVariant( !mProperty.
isActive() ) );
339 if ( !mFullDescription.isEmpty() )
341 mDefineMenu->addAction( mActionDescription );
344 mDefineMenu->addSeparator();
347 if ( mAuxiliaryStorageEnabled && mVectorLayer )
349 mDefineMenu->addAction( mActionCreateAuxiliaryField );
353 mActionCreateAuxiliaryField->setEnabled(
true );
354 mActionCreateAuxiliaryField->setChecked(
false );
358 if ( index >= 0 && alayer && mVectorLayer->
isAuxiliaryField( index, srcIndex ) )
360 mActionCreateAuxiliaryField->setEnabled(
false );
361 mActionCreateAuxiliaryField->setChecked(
true );
365 bool fieldActive =
false;
366 if ( !mDataTypesString.isEmpty() )
368 QAction *fieldTitleAct = mDefineMenu->addAction( tr(
"Attribute Field" ) );
369 fieldTitleAct->setFont( titlefont );
370 fieldTitleAct->setEnabled(
false );
372 mDefineMenu->addAction( mActionDataTypes );
374 mFieldsMenu->clear();
376 if ( !mFieldNameList.isEmpty() )
379 for (
int j = 0; j < mFieldNameList.count(); ++j )
381 QString fldname = mFieldNameList.at( j );
382 QAction *act = mFieldsMenu->addAction( mFieldDisplayNameList.at( j ) );
383 act->setIcon( mFieldIcons.at( j ) );
384 act->setData( QVariant( fldname ) );
385 if ( mFieldName == fldname )
387 act->setCheckable(
true );
395 QAction *act = mFieldsMenu->addAction( tr(
"No matching field types found" ) );
396 act->setEnabled(
false );
399 mDefineMenu->addSeparator();
402 mFieldsMenu->menuAction()->setCheckable(
true );
405 bool colorActive =
false;
406 mColorsMenu->clear();
411 QAction *colorTitleAct = mDefineMenu->addAction( tr(
"Project Color" ) );
412 colorTitleAct->setFont( titlefont );
413 colorTitleAct->setEnabled(
false );
415 QList<QgsProjectColorScheme *> projectSchemes;
417 if ( projectSchemes.length() > 0 )
421 for (
const auto &color : colors )
423 if ( color.second.isEmpty() )
427 QAction *act = mColorsMenu->addAction( color.second );
428 act->setIcon( icon );
431 act->setCheckable(
true );
432 act->setChecked(
true );
438 if ( mColorsMenu->actions().isEmpty() )
440 QAction *act = mColorsMenu->addAction( tr(
"No colors set" ) );
441 act->setEnabled(
false );
444 mDefineMenu->addAction( mActionColors );
445 mColorsMenu->menuAction()->setCheckable(
true );
446 mColorsMenu->menuAction()->setChecked( colorActive && !mProperty.
transformer() );
448 mDefineMenu->addSeparator();
451 QAction *exprTitleAct = mDefineMenu->addAction( tr(
"Expression" ) );
452 exprTitleAct->setFont( titlefont );
453 exprTitleAct->setEnabled(
false );
455 mVariablesMenu->clear();
456 bool variableActive =
false;
457 if ( mExpressionContextGenerator )
462 const auto constVariables = variables;
463 for (
const QString &variable : constVariables )
467 if ( variable.startsWith(
'_' ) )
470 QAction *act = mVariablesMenu->addAction( variable );
471 act->setData( QVariant( variable ) );
475 act->setCheckable(
true );
476 act->setChecked(
true );
477 variableActive =
true;
482 if ( mVariablesMenu->actions().isEmpty() )
484 QAction *act = mVariablesMenu->addAction( tr(
"No variables set" ) );
485 act->setEnabled(
false );
488 mDefineMenu->addAction( mActionVariables );
489 mVariablesMenu->menuAction()->setCheckable(
true );
490 mVariablesMenu->menuAction()->setChecked( variableActive && !mProperty.
transformer() );
494 QString expString = mExpressionString;
495 if ( expString.length() > 35 )
497 expString.truncate( 35 );
498 expString.append( QChar( 0x2026 ) );
501 expString.prepend( tr(
"Current: " ) );
503 if ( !mActionExpression )
505 mActionExpression =
new QAction( expString,
this );
506 mActionExpression->setCheckable(
true );
510 mActionExpression->setText( expString );
512 mDefineMenu->addAction( mActionExpression );
515 mDefineMenu->addAction( mActionExpDialog );
516 mDefineMenu->addAction( mActionCopyExpr );
517 mDefineMenu->addAction( mActionPasteExpr );
521 mDefineMenu->addAction( mActionExpDialog );
522 mDefineMenu->addAction( mActionPasteExpr );
525 if ( hasExp || !mFieldName.isEmpty() )
527 mDefineMenu->addSeparator();
528 mDefineMenu->addAction( mActionClearExpr );
533 mDefineMenu->addSeparator();
534 mActionAssistant->setCheckable( mProperty.
transformer() );
535 mActionAssistant->setChecked( mProperty.
transformer() );
536 mDefineMenu->addAction( mActionAssistant );
540void QgsPropertyOverrideButton::menuActionTriggered( QAction *action )
542 if ( action == mActionActive )
544 setActivePrivate( mActionActive->data().toBool() );
548 else if ( action == mActionDescription )
550 showDescriptionDialog();
552 else if ( action == mActionExpDialog )
554 showExpressionDialog();
556 else if ( action == mActionExpression )
560 setActivePrivate(
true );
565 else if ( action == mActionCopyExpr )
567 QApplication::clipboard()->setText( mExpressionString );
569 else if ( action == mActionPasteExpr )
571 QString exprString = QApplication::clipboard()->text();
572 if ( !exprString.isEmpty() )
574 mExpressionString = exprString;
577 setActivePrivate(
true );
583 else if ( action == mActionClearExpr )
585 setActivePrivate(
false );
588 mExpressionString.clear();
594 else if ( action == mActionAssistant )
598 else if ( action == mActionCreateAuxiliaryField )
602 else if ( mFieldsMenu->actions().contains( action ) )
604 if ( action->isEnabled() )
606 if ( mFieldName != action->text() )
608 mFieldName = action->data().toString();
612 setActivePrivate(
true );
618 else if ( mVariablesMenu->actions().contains( action ) )
620 if ( mExpressionString != action->text().prepend(
"@" ) )
622 mExpressionString = action->data().toString().prepend(
"@" );
626 setActivePrivate(
true );
631 else if ( mColorsMenu->actions().contains( action ) )
633 if ( mExpressionString != QStringLiteral(
"project_color('%1')" ).arg( action->text() ) )
635 mExpressionString = QStringLiteral(
"project_color('%1')" ).arg( action->text() );
639 setActivePrivate(
true );
647void QgsPropertyOverrideButton::showDescriptionDialog()
650 mv->setWindowTitle( tr(
"Data Definition Description" ) );
656void QgsPropertyOverrideButton::showExpressionDialog()
665 d.setExpectedOutputFormat( mInputDescription );
666 if ( d.exec() == QDialog::Accepted )
668 mExpressionString = d.expressionText().trimmed();
672 mProperty.
setActive( !mExpressionString.isEmpty() );
682void QgsPropertyOverrideButton::showAssistant()
702 mFieldName = this->mProperty.
field();
719 QDialog *dlg =
new QDialog(
this );
720 QString key = QStringLiteral(
"/UI/paneldialog/%1" ).arg( widget->
panelTitle() );
722 dlg->restoreGeometry( settings.
value( key ).toByteArray() );
724 dlg->setLayout(
new QVBoxLayout() );
725 dlg->layout()->addWidget( widget );
726 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
727 connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
728 connect( buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject );
729 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsPropertyOverrideButton::showHelp );
730 dlg->layout()->addWidget( buttonBox );
732 if ( dlg->exec() == QDialog::Accepted )
736 mFieldName = mProperty.
field();
743 settings.
setValue( key, dlg->saveGeometry() );
747void QgsPropertyOverrideButton::updateGui()
749 bool hasExp = !mExpressionString.isEmpty();
750 bool hasField = !mFieldName.isEmpty();
753 QString deftip = tr(
"undefined" );
759 const thread_local QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
760 QRegularExpressionMatch match = rx.match( mExpressionString );
761 if ( match.hasMatch() )
764 deftip = match.captured( 1 );
765 deftype = tr(
"project color" );
770 if ( exp.hasParserError() )
773 deftip = tr(
"Parse error: %1" ).arg( exp.parserErrorString() );
777 deftip = mExpressionString;
785 if ( !mFieldNameList.contains( mFieldName ) && !mProperty.
transformer() )
788 deftip = tr(
"'%1' field missing" ).arg( mFieldName );
799 mFullDescription = tr(
"<b><u>Data defined override</u></b><br>" );
801 mFullDescription += tr(
"<b>Active: </b>%1 <i>(ctrl|right-click toggles)</i><br>" ).arg( mProperty.
isActive() ? tr(
"yes" ) : tr(
"no" ) );
803 if ( !mUsageInfo.isEmpty() )
805 mFullDescription += tr(
"<b>Usage:</b><br>%1<br>" ).arg( mUsageInfo );
808 if ( !mInputDescription.isEmpty() )
810 mFullDescription += tr(
"<b>Expected input:</b><br>%1<br>" ).arg( mInputDescription );
813 if ( !mDataTypesString.isEmpty() )
815 mFullDescription += tr(
"<b>Valid input types:</b><br>%1<br>" ).arg( mDataTypesString );
818 if ( deftype.isEmpty() && deftip != tr(
"undefined" ) )
824 if ( deftip.length() > 75 )
826 deftip.truncate( 75 );
827 deftip.append( QChar( 0x2026 ) );
830 mFullDescription += tr(
"<b>Current definition (%1):</b><br>%2" ).arg( deftype, deftip );
832 setToolTip( mFullDescription );
836void QgsPropertyOverrideButton::setActivePrivate(
bool active )
845void QgsPropertyOverrideButton::updateSiblingWidgets(
bool state )
847 const auto constMSiblingWidgets = mSiblingWidgets;
848 for (
const SiblingWidget &sw : constMSiblingWidgets )
850 switch ( sw.mSiblingType )
853 case SiblingCheckState:
858 QAbstractButton *btn = qobject_cast< QAbstractButton * >( sw.mWidgetPointer.data() );
859 if ( btn && btn->isCheckable() )
861 btn->setChecked( sw.mNatural ? state : !state );
865 QGroupBox *grpbx = qobject_cast< QGroupBox * >( sw.mWidgetPointer.data() );
866 if ( grpbx && grpbx->isCheckable() )
868 grpbx->setChecked( sw.mNatural ? state : !state );
875 case SiblingEnableState:
877 QLineEdit *le = qobject_cast< QLineEdit * >( sw.mWidgetPointer.data() );
879 le->setReadOnly( sw.mNatural ? !state : state );
881 sw.mWidgetPointer.data()->setEnabled( sw.mNatural ? state : !state );
885 case SiblingVisibility:
887 sw.mWidgetPointer.data()->setVisible( sw.mNatural ? state : !state );
891 case SiblingExpressionText:
893 QLineEdit *le = qobject_cast<QLineEdit *>( sw.mWidgetPointer.data() );
900 QTextEdit *te = qobject_cast<QTextEdit *>( sw.mWidgetPointer.data() );
909 case SiblingLinkedWidget:
911 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( sw.mWidgetPointer.data() ) )
915 const thread_local QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
916 QRegularExpressionMatch match = rx.match( mExpressionString );
917 if ( match.hasMatch() )
919 cb->linkToProjectColor( match.captured( 1 ) );
924 cb->linkToProjectColor( QString() );
948 mExpressionContextGenerator = generator;
953 for (
const SiblingWidget &sw : std::as_const( mSiblingWidgets ) )
955 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingLinkedWidget )
958 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingLinkedWidget ) );
960 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( widget ) )
972void QgsPropertyOverrideButton::showHelp()
974 QgsHelp::openHelp( QStringLiteral(
"introduction/general_tools.html#data-defined" ) );
Abstract base class for QgsPropertyCollection like objects.
virtual QgsProperty property(int key) const =0
Returns a matching property from the collection, if one exists.
Extends QApplication to provide access to QGIS specific resources such as theme paths,...
static QgsColorSchemeRegistry * colorSchemeRegistry()
Returns the application's color scheme registry, used for managing color schemes.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Class allowing to manage the auxiliary storage for a vector layer.
QList< QgsColorScheme * > schemes() const
Returns all color schemes in the registry.
A generic dialog for building expression strings.
Abstract interface for generating an expression context.
virtual QgsExpressionContext createExpressionContext() const =0
This method needs to be reimplemented in all classes which implement this interface and return an exp...
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
bool isReadOnly(const QString &name) const
Returns whether a variable is read only, and should not be modifiable by users.
QStringList variableNames() const
Returns a list of variables names set by all scopes in the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
Encapsulate a field in an attribute table or data source.
Container of fields for a vector layer.
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
QIcon iconForField(int fieldIdx, bool considerOrigin=false) const
Returns an icon corresponding to a field index, based on the field's type and source.
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
A generic message view for displaying QGIS messages.
void setMessageAsHtml(const QString &msg)
A color scheme which contains project specific colors set through project properties dialog.
QgsNamedColorList fetchColors(const QString &context=QString(), const QColor &baseColor=QColor()) override
Gets a list of colors from the scheme.
Definition for a property.
StandardPropertyTemplate standardTemplate() const
Returns the property's standard template, if applicable.
QString helpText() const
Helper text for using the property, including a description of the valid values for the property.
DataType dataType() const
Returns the allowable field/value data type for the property.
@ ColorNoAlpha
Color with no alpha channel.
@ ColorWithAlpha
Color with alpha channel.
QString name() const
Returns the name of the property.
bool supportsAssistant() const
Returns true if the property is of a type which is compatible with property override assistants.
@ DataTypeString
Property requires a string value.
@ DataTypeBoolean
Property requires a boolean value.
@ DataTypeNumeric
Property requires a numeric value.
A store for object properties.
@ ExpressionBasedProperty
Expression based property (QgsExpressionBasedProperty)
@ StaticProperty
Static property (QgsStaticProperty)
@ FieldBasedProperty
Field based property (QgsFieldBasedProperty)
@ InvalidProperty
Invalid (not set) property.
bool isProjectColor() const
Returns true if the property is set to a linked project color.
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
bool convertToTransformer()
Attempts to convert an existing expression based property to a base expression with corresponding tra...
void setTransformer(QgsPropertyTransformer *transformer)
Sets an optional transformer to use for manipulating the calculated values for the property.
void setStaticValue(const QVariant &value)
Sets the static value for the property.
QString field() const
Returns the current field name the property references.
const QgsPropertyTransformer * transformer() const
Returns the existing transformer used for manipulating the calculated values for the property,...
QVariant value(const QgsExpressionContext &context, const QVariant &defaultValue=QVariant(), bool *ok=nullptr) const
Calculates the current value of the property, including any transforms which are set for the property...
bool isActive() const
Returns whether the property is currently active.
void setField(const QString &field)
Sets the field name the property references.
QVariant staticValue() const
Returns the current static value for the property.
Type propertyType() const
Returns the property type.
void setExpressionString(const QString &expression)
Sets the expression to use for the property value.
void setActive(bool active)
Sets whether the property is currently active.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Represents a vector layer which manages a vector based data sets.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
bool isAuxiliaryField(int index, int &srcIndex) const
Returns true if the field comes from the auxiliary layer, false otherwise.
QList< QPair< QColor, QString > > QgsNamedColorList
List of colors paired with a friendly display name identifying the color.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.