35#include <QRegularExpression>
39 : QToolButton( parent )
40 , mVectorLayer( layer )
43 setFocusPolicy( Qt::StrongFocus );
45 QString ss = QStringLiteral(
"QgsPropertyOverrideButton { background: none; border: 1px solid rgba(0, 0, 0, 0%); } QgsPropertyOverrideButton:focus { border: 1px solid palette(highlight); }" );
47 ss += QLatin1String(
"QgsPropertyOverrideButton::menu-indicator { width: 5px; }" );
54 setFixedSize( 2 *
static_cast< int >( 1.25 * iconSize / 2.0 ), 2 *
static_cast< int >( iconSize * 1.1 / 2.0 ) );
56 setIconSize( QSize( iconSize, iconSize ) );
57 setPopupMode( QToolButton::InstantPopup );
61 mDefineMenu =
new QMenu(
this );
62 connect( mDefineMenu, &QMenu::aboutToShow,
this, &QgsPropertyOverrideButton::aboutToShowMenu );
63 connect( mDefineMenu, &QMenu::triggered,
this, &QgsPropertyOverrideButton::menuActionTriggered );
64 setMenu( mDefineMenu );
66 mFieldsMenu =
new QMenu(
this );
67 mActionDataTypes =
new QAction(
this );
69 mActionDataTypes->setMenu( mFieldsMenu );
71 mActionVariables =
new QAction( tr(
"Variable" ),
this );
72 mVariablesMenu =
new QMenu(
this );
73 mActionVariables->setMenu( mVariablesMenu );
75 mActionColors =
new QAction( tr(
"Color" ),
this );
76 mColorsMenu =
new QMenu(
this );
77 mActionColors->setMenu( mColorsMenu );
79 mActionActive =
new QAction(
this );
80 QFont f = mActionActive->font();
82 mActionActive->setFont( f );
84 mActionDescription =
new QAction( tr(
"Description…" ),
this );
86 mActionCreateAuxiliaryField =
new QAction( tr(
"Store Data in the Project" ),
this );
87 mActionCreateAuxiliaryField->setCheckable(
true );
89 mActionExpDialog =
new QAction( tr(
"Edit…" ),
this );
90 mActionExpression =
nullptr;
91 mActionPasteExpr =
new QAction( tr(
"Paste" ),
this );
92 mActionCopyExpr =
new QAction( tr(
"Copy" ),
this );
93 mActionClearExpr =
new QAction( tr(
"Clear" ),
this );
94 mActionAssistant =
new QAction( tr(
"Assistant…" ),
this );
95 QFont assistantFont = mActionAssistant->font();
96 assistantFont.setBold(
true );
97 mActionAssistant->setFont( assistantFont );
98 mDefineMenu->addAction( mActionAssistant );
109 mVectorLayer = layer;
110 mAuxiliaryStorageEnabled = auxiliaryStorageEnabled;
114 mDefinition = definition;
115 mDataTypes = mDefinition.
dataType();
117 mInputDescription = mDefinition.
helpText();
118 mFullDescription.clear();
122 mDataTypesString.clear();
125 switch ( mDataTypes )
128 ts << tr(
"boolean" );
133 ts << tr(
"double" );
137 ts << tr(
"string" );
143 mDataTypesString = ts.join( QLatin1String(
", " ) );
144 mActionDataTypes->setText( tr(
"Field type: " ) + mDataTypesString );
160 mFieldNameList.clear();
161 mFieldDisplayNameList.clear();
171 bool fieldMatch =
false;
172 switch ( mDataTypes )
179 fieldMatch = f.isNumeric() || f.type() == QMetaType::Type::QString;
183 fieldMatch = f.type() == QMetaType::Type::QString;
189 mFieldNameList << f.name();
190 mFieldDisplayNameList << f.displayNameWithAlias();
205 mVectorLayer = layer;
212 const auto constMSiblingWidgets = mSiblingWidgets;
213 for (
const SiblingWidget &sw : constMSiblingWidgets )
215 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingCheckState )
218 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingCheckState, natural ) );
224 const auto constMSiblingWidgets = mSiblingWidgets;
225 for (
const SiblingWidget &sw : constMSiblingWidgets )
227 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingEnableState )
230 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingEnableState, natural ) );
236 const auto constMSiblingWidgets = mSiblingWidgets;
237 for (
const SiblingWidget &sw : constMSiblingWidgets )
239 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingVisibility )
242 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingVisibility, natural ) );
248 const auto constMSiblingWidgets = mSiblingWidgets;
249 for (
const SiblingWidget &sw : constMSiblingWidgets )
251 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingExpressionText )
254 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingExpressionText ) );
262 if ( ( event->modifiers() & ( Qt::ControlModifier ) )
263 || event->button() == Qt::RightButton )
265 setActivePrivate( !mProperty.
isActive() );
273 if ( event->button() == Qt::MiddleButton )
275 showExpressionDialog();
280 QToolButton::mousePressEvent( event );
294 mFieldName =
property.field();
299 mExpressionString =
property.expressionString();
307 mExpressionString.clear();
309 mProperty = property;
316void QgsPropertyOverrideButton::aboutToShowMenu()
318 mDefineMenu->clear();
322 bool hasExp = !mExpressionString.isEmpty();
323 QString ddTitle = tr(
"Data defined override" );
325 QAction *ddTitleAct = mDefineMenu->addAction( ddTitle );
326 QFont titlefont = ddTitleAct->font();
327 titlefont.setItalic(
true );
328 ddTitleAct->setFont( titlefont );
329 ddTitleAct->setEnabled(
false );
331 bool addActiveAction =
false;
336 addActiveAction = !exp.hasParserError();
341 addActiveAction = mFieldNameList.contains( mFieldName );
344 if ( addActiveAction )
347 mDefineMenu->addAction( mActionActive );
348 mActionActive->setText( mProperty.
isActive() ? tr(
"Deactivate" ) : tr(
"Activate" ) );
349 mActionActive->setData( QVariant( !mProperty.
isActive() ) );
352 if ( !mFullDescription.isEmpty() )
354 mDefineMenu->addAction( mActionDescription );
357 mDefineMenu->addSeparator();
360 if ( mAuxiliaryStorageEnabled && mVectorLayer )
362 mDefineMenu->addAction( mActionCreateAuxiliaryField );
366 mActionCreateAuxiliaryField->setEnabled(
true );
367 mActionCreateAuxiliaryField->setChecked(
false );
371 if ( index >= 0 && alayer && mVectorLayer->
isAuxiliaryField( index, srcIndex ) )
373 mActionCreateAuxiliaryField->setEnabled(
false );
374 mActionCreateAuxiliaryField->setChecked(
true );
378 bool fieldActive =
false;
379 if ( !mDataTypesString.isEmpty() )
381 QAction *fieldTitleAct = mDefineMenu->addAction( tr(
"Attribute Field" ) );
382 fieldTitleAct->setFont( titlefont );
383 fieldTitleAct->setEnabled(
false );
385 mDefineMenu->addAction( mActionDataTypes );
387 mFieldsMenu->clear();
389 if ( !mFieldNameList.isEmpty() )
392 for (
int j = 0; j < mFieldNameList.count(); ++j )
394 QString fldname = mFieldNameList.at( j );
395 QAction *act = mFieldsMenu->addAction( mFieldDisplayNameList.at( j ) );
396 act->setIcon( mFieldIcons.at( j ) );
397 act->setData( QVariant( fldname ) );
398 if ( mFieldName == fldname )
400 act->setCheckable(
true );
408 QAction *act = mFieldsMenu->addAction( tr(
"No matching field types found" ) );
409 act->setEnabled(
false );
412 mDefineMenu->addSeparator();
415 mFieldsMenu->menuAction()->setCheckable(
true );
418 bool colorActive =
false;
419 mColorsMenu->clear();
424 QAction *colorTitleAct = mDefineMenu->addAction( tr(
"Project Color" ) );
425 colorTitleAct->setFont( titlefont );
426 colorTitleAct->setEnabled(
false );
428 QList<QgsProjectColorScheme *> projectSchemes;
430 if ( projectSchemes.length() > 0 )
434 for (
const auto &color : colors )
436 if ( color.second.isEmpty() )
440 QAction *act = mColorsMenu->addAction( color.second );
441 act->setIcon( icon );
444 act->setCheckable(
true );
445 act->setChecked(
true );
451 if ( mColorsMenu->actions().isEmpty() )
453 QAction *act = mColorsMenu->addAction( tr(
"No colors set" ) );
454 act->setEnabled(
false );
457 mDefineMenu->addAction( mActionColors );
458 mColorsMenu->menuAction()->setCheckable(
true );
459 mColorsMenu->menuAction()->setChecked( colorActive && !mProperty.
transformer() );
461 mDefineMenu->addSeparator();
464 QAction *exprTitleAct = mDefineMenu->addAction( tr(
"Expression" ) );
465 exprTitleAct->setFont( titlefont );
466 exprTitleAct->setEnabled(
false );
468 mVariablesMenu->clear();
469 bool variableActive =
false;
470 if ( mExpressionContextGenerator )
475 const auto constVariables = variables;
476 for (
const QString &variable : constVariables )
480 if ( variable.startsWith(
'_' ) )
483 QAction *act = mVariablesMenu->addAction( variable );
484 act->setData( QVariant( variable ) );
488 act->setCheckable(
true );
489 act->setChecked(
true );
490 variableActive =
true;
495 if ( mVariablesMenu->actions().isEmpty() )
497 QAction *act = mVariablesMenu->addAction( tr(
"No variables set" ) );
498 act->setEnabled(
false );
501 mDefineMenu->addAction( mActionVariables );
502 mVariablesMenu->menuAction()->setCheckable(
true );
503 mVariablesMenu->menuAction()->setChecked( variableActive && !mProperty.
transformer() );
507 QString expString = mExpressionString;
508 if ( expString.length() > 35 )
510 expString.truncate( 35 );
511 expString.append( QChar( 0x2026 ) );
514 expString.prepend( tr(
"Current: " ) );
516 if ( !mActionExpression )
518 mActionExpression =
new QAction( expString,
this );
519 mActionExpression->setCheckable(
true );
523 mActionExpression->setText( expString );
525 mDefineMenu->addAction( mActionExpression );
528 mDefineMenu->addAction( mActionExpDialog );
529 mDefineMenu->addAction( mActionCopyExpr );
530 mDefineMenu->addAction( mActionPasteExpr );
534 mDefineMenu->addAction( mActionExpDialog );
535 mDefineMenu->addAction( mActionPasteExpr );
538 if ( hasExp || !mFieldName.isEmpty() )
540 mDefineMenu->addSeparator();
541 mDefineMenu->addAction( mActionClearExpr );
546 mDefineMenu->addSeparator();
547 mActionAssistant->setCheckable( mProperty.
transformer() );
548 mActionAssistant->setChecked( mProperty.
transformer() );
549 mDefineMenu->addAction( mActionAssistant );
553void QgsPropertyOverrideButton::menuActionTriggered( QAction *action )
555 if ( action == mActionActive )
557 setActivePrivate( mActionActive->data().toBool() );
561 else if ( action == mActionDescription )
563 showDescriptionDialog();
565 else if ( action == mActionExpDialog )
567 showExpressionDialog();
569 else if ( action == mActionExpression )
573 setActivePrivate(
true );
578 else if ( action == mActionCopyExpr )
580 QApplication::clipboard()->setText( mExpressionString );
582 else if ( action == mActionPasteExpr )
584 QString exprString = QApplication::clipboard()->text();
585 if ( !exprString.isEmpty() )
587 mExpressionString = exprString;
590 setActivePrivate(
true );
596 else if ( action == mActionClearExpr )
598 setActivePrivate(
false );
601 mExpressionString.clear();
607 else if ( action == mActionAssistant )
611 else if ( action == mActionCreateAuxiliaryField )
615 else if ( mFieldsMenu->actions().contains( action ) )
617 if ( action->isEnabled() )
619 if ( mFieldName != action->text() )
621 mFieldName = action->data().toString();
625 setActivePrivate(
true );
631 else if ( mVariablesMenu->actions().contains( action ) )
633 if ( mExpressionString != action->text().prepend(
"@" ) )
635 mExpressionString = action->data().toString().prepend(
"@" );
639 setActivePrivate(
true );
644 else if ( mColorsMenu->actions().contains( action ) )
646 if ( mExpressionString != QStringLiteral(
"project_color('%1')" ).arg( action->text() ) )
648 mExpressionString = QStringLiteral(
"project_color('%1')" ).arg( action->text() );
652 setActivePrivate(
true );
660void QgsPropertyOverrideButton::showDescriptionDialog()
663 mv->setWindowTitle( tr(
"Data Definition Description" ) );
669void QgsPropertyOverrideButton::showExpressionDialog()
678 d.setExpectedOutputFormat( mInputDescription );
679 if ( d.exec() == QDialog::Accepted )
681 mExpressionString = d.expressionText().trimmed();
685 mProperty.
setActive( !mExpressionString.isEmpty() );
695void QgsPropertyOverrideButton::showAssistant()
715 mFieldName = this->mProperty.
field();
732 QDialog *dlg =
new QDialog(
this );
733 QString key = QStringLiteral(
"/UI/paneldialog/%1" ).arg( widget->
panelTitle() );
735 dlg->restoreGeometry( settings.
value( key ).toByteArray() );
737 dlg->setLayout(
new QVBoxLayout() );
738 dlg->layout()->addWidget( widget );
739 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
740 connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
741 connect( buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject );
742 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsPropertyOverrideButton::showHelp );
743 dlg->layout()->addWidget( buttonBox );
745 if ( dlg->exec() == QDialog::Accepted )
749 mFieldName = mProperty.
field();
756 settings.
setValue( key, dlg->saveGeometry() );
760void QgsPropertyOverrideButton::updateGui()
762 bool hasExp = !mExpressionString.isEmpty();
763 bool hasField = !mFieldName.isEmpty();
766 QString deftip = tr(
"undefined" );
772 const thread_local QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
773 QRegularExpressionMatch match = rx.match( mExpressionString );
774 if ( match.hasMatch() )
777 deftip = match.captured( 1 );
778 deftype = tr(
"project color" );
783 if ( exp.hasParserError() )
786 deftip = tr(
"Parse error: %1" ).arg( exp.parserErrorString() );
790 deftip = mExpressionString;
798 if ( !mFieldNameList.contains( mFieldName ) && !mProperty.
transformer() )
801 deftip = tr(
"'%1' field missing" ).arg( mFieldName );
812 mFullDescription = tr(
"<b><u>Data defined override</u></b><br>" );
814 mFullDescription += tr(
"<b>Active: </b>%1 <i>(ctrl|right-click toggles)</i><br>" ).arg( mProperty.
isActive() ? tr(
"yes" ) : tr(
"no" ) );
816 if ( !mUsageInfo.isEmpty() )
818 mFullDescription += tr(
"<b>Usage:</b><br>%1<br>" ).arg( mUsageInfo );
821 if ( !mInputDescription.isEmpty() )
823 mFullDescription += tr(
"<b>Expected input:</b><br>%1<br>" ).arg( mInputDescription );
826 if ( !mDataTypesString.isEmpty() )
828 mFullDescription += tr(
"<b>Valid input types:</b><br>%1<br>" ).arg( mDataTypesString );
831 if ( deftype.isEmpty() && deftip != tr(
"undefined" ) )
837 if ( deftip.length() > 75 )
839 deftip.truncate( 75 );
840 deftip.append( QChar( 0x2026 ) );
843 mFullDescription += tr(
"<b>Current definition (%1):</b><br>%2" ).arg( deftype, deftip );
845 setToolTip( mFullDescription );
849void QgsPropertyOverrideButton::setActivePrivate(
bool active )
858void QgsPropertyOverrideButton::updateSiblingWidgets(
bool state )
860 const auto constMSiblingWidgets = mSiblingWidgets;
861 for (
const SiblingWidget &sw : constMSiblingWidgets )
863 switch ( sw.mSiblingType )
866 case SiblingCheckState:
871 QAbstractButton *btn = qobject_cast< QAbstractButton * >( sw.mWidgetPointer.data() );
872 if ( btn && btn->isCheckable() )
874 btn->setChecked( sw.mNatural ? state : !state );
878 QGroupBox *grpbx = qobject_cast< QGroupBox * >( sw.mWidgetPointer.data() );
879 if ( grpbx && grpbx->isCheckable() )
881 grpbx->setChecked( sw.mNatural ? state : !state );
888 case SiblingEnableState:
890 QLineEdit *le = qobject_cast< QLineEdit * >( sw.mWidgetPointer.data() );
892 le->setReadOnly( sw.mNatural ? !state : state );
894 sw.mWidgetPointer.data()->setEnabled( sw.mNatural ? state : !state );
898 case SiblingVisibility:
900 sw.mWidgetPointer.data()->setVisible( sw.mNatural ? state : !state );
904 case SiblingExpressionText:
906 QLineEdit *le = qobject_cast<QLineEdit *>( sw.mWidgetPointer.data() );
913 QTextEdit *te = qobject_cast<QTextEdit *>( sw.mWidgetPointer.data() );
922 case SiblingLinkedWidget:
924 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( sw.mWidgetPointer.data() ) )
928 const thread_local QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
929 QRegularExpressionMatch match = rx.match( mExpressionString );
930 if ( match.hasMatch() )
932 cb->linkToProjectColor( match.captured( 1 ) );
937 cb->linkToProjectColor( QString() );
961 mExpressionContextGenerator = generator;
966 for (
const SiblingWidget &sw : std::as_const( mSiblingWidgets ) )
968 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingLinkedWidget )
971 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingLinkedWidget ) );
973 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( widget ) )
985void QgsPropertyOverrideButton::showHelp()
987 QgsHelp::openHelp( QStringLiteral(
"introduction/general_tools.html#data-defined" ) );
@ Invalid
Invalid (not set) property.
@ Field
Field based property.
@ Expression
Expression based property.
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.
Q_INVOKABLE 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.
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.
Qgis::PropertyType propertyType() const
Returns the property type.
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.
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.
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.