17#include "moc_qgspropertyoverridebutton.cpp"
36#include <QRegularExpression>
40 : QToolButton( parent )
41 , mVectorLayer( layer )
44 setFocusPolicy( Qt::StrongFocus );
46 QString ss = QStringLiteral(
"QgsPropertyOverrideButton { background: none; border: 1px solid rgba(0, 0, 0, 0%); } QgsPropertyOverrideButton:focus { border: 1px solid palette(highlight); }" );
48 ss += QLatin1String(
"QgsPropertyOverrideButton::menu-indicator { width: 5px; }" );
55 setFixedSize( 2 *
static_cast< int >( 1.25 * iconSize / 2.0 ), 2 *
static_cast< int >( iconSize * 1.1 / 2.0 ) );
57 setIconSize( QSize( iconSize, iconSize ) );
58 setPopupMode( QToolButton::InstantPopup );
62 mDefineMenu =
new QMenu(
this );
63 connect( mDefineMenu, &QMenu::aboutToShow,
this, &QgsPropertyOverrideButton::aboutToShowMenu );
64 connect( mDefineMenu, &QMenu::triggered,
this, &QgsPropertyOverrideButton::menuActionTriggered );
65 setMenu( mDefineMenu );
67 mFieldsMenu =
new QMenu(
this );
68 mActionDataTypes =
new QAction(
this );
70 mActionDataTypes->setMenu( mFieldsMenu );
72 mActionVariables =
new QAction( tr(
"Variable" ),
this );
73 mVariablesMenu =
new QMenu(
this );
74 mActionVariables->setMenu( mVariablesMenu );
76 mActionColors =
new QAction( tr(
"Color" ),
this );
77 mColorsMenu =
new QMenu(
this );
78 mActionColors->setMenu( mColorsMenu );
80 mActionActive =
new QAction(
this );
81 QFont f = mActionActive->font();
83 mActionActive->setFont( f );
85 mActionDescription =
new QAction( tr(
"Description…" ),
this );
87 mActionCreateAuxiliaryField =
new QAction( tr(
"Store Data in the Project" ),
this );
88 mActionCreateAuxiliaryField->setCheckable(
true );
90 mActionExpDialog =
new QAction( tr(
"Edit…" ),
this );
91 mActionExpression =
nullptr;
92 mActionPasteExpr =
new QAction( tr(
"Paste" ),
this );
93 mActionCopyExpr =
new QAction( tr(
"Copy" ),
this );
94 mActionClearExpr =
new QAction( tr(
"Clear" ),
this );
95 mActionAssistant =
new QAction( tr(
"Assistant…" ),
this );
96 QFont assistantFont = mActionAssistant->font();
97 assistantFont.setBold(
true );
98 mActionAssistant->setFont( assistantFont );
99 mDefineMenu->addAction( mActionAssistant );
110 mVectorLayer = layer;
111 mAuxiliaryStorageEnabled = auxiliaryStorageEnabled;
115 mDefinition = definition;
116 mDataTypes = mDefinition.
dataType();
118 mInputDescription = mDefinition.
helpText();
119 mFullDescription.clear();
123 mDataTypesString.clear();
126 switch ( mDataTypes )
129 ts << tr(
"boolean" );
134 ts << tr(
"double" );
138 ts << tr(
"string" );
144 mDataTypesString = ts.join( QLatin1String(
", " ) );
145 mActionDataTypes->setText( tr(
"Field type: " ) + mDataTypesString );
161 mFieldNameList.clear();
162 mFieldDisplayNameList.clear();
172 bool fieldMatch =
false;
173 switch ( mDataTypes )
180 fieldMatch = f.isNumeric() || f.type() == QMetaType::Type::QString;
184 fieldMatch = f.type() == QMetaType::Type::QString;
190 mFieldNameList << f.name();
191 mFieldDisplayNameList << f.displayNameWithAlias();
206 mVectorLayer = layer;
213 const auto constMSiblingWidgets = mSiblingWidgets;
214 for (
const SiblingWidget &sw : constMSiblingWidgets )
216 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingCheckState )
219 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingCheckState, natural ) );
225 const auto constMSiblingWidgets = mSiblingWidgets;
226 for (
const SiblingWidget &sw : constMSiblingWidgets )
228 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingEnableState )
231 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingEnableState, natural ) );
237 const auto constMSiblingWidgets = mSiblingWidgets;
238 for (
const SiblingWidget &sw : constMSiblingWidgets )
240 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingVisibility )
243 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingVisibility, natural ) );
249 const auto constMSiblingWidgets = mSiblingWidgets;
250 for (
const SiblingWidget &sw : constMSiblingWidgets )
252 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingExpressionText )
255 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingExpressionText ) );
263 if ( ( event->modifiers() & ( Qt::ControlModifier ) )
264 || event->button() == Qt::RightButton )
266 setActivePrivate( !mProperty.
isActive() );
274 if ( event->button() == Qt::MiddleButton )
276 showExpressionDialog();
281 QToolButton::mousePressEvent( event );
295 mFieldName =
property.field();
300 mExpressionString =
property.expressionString();
308 mExpressionString.clear();
310 mProperty = property;
317void QgsPropertyOverrideButton::aboutToShowMenu()
319 mDefineMenu->clear();
323 bool hasExp = !mExpressionString.isEmpty();
324 QString ddTitle = tr(
"Data defined override" );
326 QAction *ddTitleAct = mDefineMenu->addAction( ddTitle );
327 QFont titlefont = ddTitleAct->font();
328 titlefont.setItalic(
true );
329 ddTitleAct->setFont( titlefont );
330 ddTitleAct->setEnabled(
false );
332 bool addActiveAction =
false;
337 addActiveAction = !exp.hasParserError();
342 addActiveAction = mFieldNameList.contains( mFieldName );
345 if ( addActiveAction )
348 mDefineMenu->addAction( mActionActive );
349 mActionActive->setText( mProperty.
isActive() ? tr(
"Deactivate" ) : tr(
"Activate" ) );
350 mActionActive->setData( QVariant( !mProperty.
isActive() ) );
353 if ( !mFullDescription.isEmpty() )
355 mDefineMenu->addAction( mActionDescription );
358 mDefineMenu->addSeparator();
361 if ( mAuxiliaryStorageEnabled && mVectorLayer )
363 mDefineMenu->addAction( mActionCreateAuxiliaryField );
367 mActionCreateAuxiliaryField->setEnabled(
true );
368 mActionCreateAuxiliaryField->setChecked(
false );
372 if ( index >= 0 && alayer && mVectorLayer->
isAuxiliaryField( index, srcIndex ) )
374 mActionCreateAuxiliaryField->setEnabled(
false );
375 mActionCreateAuxiliaryField->setChecked(
true );
379 bool fieldActive =
false;
380 if ( !mDataTypesString.isEmpty() )
382 QAction *fieldTitleAct = mDefineMenu->addAction( tr(
"Attribute Field" ) );
383 fieldTitleAct->setFont( titlefont );
384 fieldTitleAct->setEnabled(
false );
386 mDefineMenu->addAction( mActionDataTypes );
388 mFieldsMenu->clear();
390 if ( !mFieldNameList.isEmpty() )
393 for (
int j = 0; j < mFieldNameList.count(); ++j )
395 QString fldname = mFieldNameList.at( j );
396 QAction *act = mFieldsMenu->addAction( mFieldDisplayNameList.at( j ) );
397 act->setIcon( mFieldIcons.at( j ) );
398 act->setData( QVariant( fldname ) );
399 if ( mFieldName == fldname )
401 act->setCheckable(
true );
409 QAction *act = mFieldsMenu->addAction( tr(
"No matching field types found" ) );
410 act->setEnabled(
false );
413 mDefineMenu->addSeparator();
416 mFieldsMenu->menuAction()->setCheckable(
true );
419 bool colorActive =
false;
420 mColorsMenu->clear();
425 QAction *colorTitleAct = mDefineMenu->addAction( tr(
"Project Color" ) );
426 colorTitleAct->setFont( titlefont );
427 colorTitleAct->setEnabled(
false );
429 QList<QgsProjectColorScheme *> projectSchemes;
431 if ( projectSchemes.length() > 0 )
435 for (
const auto &color : colors )
437 if ( color.second.isEmpty() )
441 QAction *act = mColorsMenu->addAction( color.second );
442 act->setIcon( icon );
445 act->setCheckable(
true );
446 act->setChecked(
true );
452 if ( mColorsMenu->actions().isEmpty() )
454 QAction *act = mColorsMenu->addAction( tr(
"No colors set" ) );
455 act->setEnabled(
false );
458 mDefineMenu->addAction( mActionColors );
459 mColorsMenu->menuAction()->setCheckable(
true );
460 mColorsMenu->menuAction()->setChecked( colorActive && !mProperty.
transformer() );
462 mDefineMenu->addSeparator();
465 QAction *exprTitleAct = mDefineMenu->addAction( tr(
"Expression" ) );
466 exprTitleAct->setFont( titlefont );
467 exprTitleAct->setEnabled(
false );
469 mVariablesMenu->clear();
470 bool variableActive =
false;
471 if ( mExpressionContextGenerator )
476 const auto constVariables = variables;
477 for (
const QString &variable : constVariables )
481 if ( variable.startsWith(
'_' ) )
484 QAction *act = mVariablesMenu->addAction( variable );
485 act->setData( QVariant( variable ) );
489 act->setCheckable(
true );
490 act->setChecked(
true );
491 variableActive =
true;
496 if ( mVariablesMenu->actions().isEmpty() )
498 QAction *act = mVariablesMenu->addAction( tr(
"No variables set" ) );
499 act->setEnabled(
false );
502 mDefineMenu->addAction( mActionVariables );
503 mVariablesMenu->menuAction()->setCheckable(
true );
504 mVariablesMenu->menuAction()->setChecked( variableActive && !mProperty.
transformer() );
508 QString expString = mExpressionString;
509 if ( expString.length() > 35 )
511 expString.truncate( 35 );
512 expString.append( QChar( 0x2026 ) );
515 expString.prepend( tr(
"Current: " ) );
517 if ( !mActionExpression )
519 mActionExpression =
new QAction( expString,
this );
520 mActionExpression->setCheckable(
true );
524 mActionExpression->setText( expString );
526 mDefineMenu->addAction( mActionExpression );
529 mDefineMenu->addAction( mActionExpDialog );
530 mDefineMenu->addAction( mActionCopyExpr );
531 mDefineMenu->addAction( mActionPasteExpr );
535 mDefineMenu->addAction( mActionExpDialog );
536 mDefineMenu->addAction( mActionPasteExpr );
539 if ( hasExp || !mFieldName.isEmpty() )
541 mDefineMenu->addSeparator();
542 mDefineMenu->addAction( mActionClearExpr );
547 mDefineMenu->addSeparator();
548 mActionAssistant->setCheckable( mProperty.
transformer() );
549 mActionAssistant->setChecked( mProperty.
transformer() );
550 mDefineMenu->addAction( mActionAssistant );
554void QgsPropertyOverrideButton::menuActionTriggered( QAction *action )
556 if ( action == mActionActive )
558 setActivePrivate( mActionActive->data().toBool() );
562 else if ( action == mActionDescription )
564 showDescriptionDialog();
566 else if ( action == mActionExpDialog )
568 showExpressionDialog();
570 else if ( action == mActionExpression )
574 setActivePrivate(
true );
579 else if ( action == mActionCopyExpr )
581 QApplication::clipboard()->setText( mExpressionString );
583 else if ( action == mActionPasteExpr )
585 QString exprString = QApplication::clipboard()->text();
586 if ( !exprString.isEmpty() )
588 mExpressionString = exprString;
591 setActivePrivate(
true );
597 else if ( action == mActionClearExpr )
599 setActivePrivate(
false );
602 mExpressionString.clear();
608 else if ( action == mActionAssistant )
612 else if ( action == mActionCreateAuxiliaryField )
616 else if ( mFieldsMenu->actions().contains( action ) )
618 if ( action->isEnabled() )
620 if ( mFieldName != action->text() )
622 mFieldName = action->data().toString();
626 setActivePrivate(
true );
632 else if ( mVariablesMenu->actions().contains( action ) )
634 if ( mExpressionString != action->text().prepend(
"@" ) )
636 mExpressionString = action->data().toString().prepend(
"@" );
640 setActivePrivate(
true );
645 else if ( mColorsMenu->actions().contains( action ) )
647 if ( getColor() != action->text() )
649 mExpressionString = QStringLiteral(
"project_color_object('%1')" ).arg( action->text() );
653 setActivePrivate(
true );
661void QgsPropertyOverrideButton::showDescriptionDialog()
664 mv->setWindowTitle( tr(
"Data Definition Description" ) );
670void QgsPropertyOverrideButton::showExpressionDialog()
679 d.setExpectedOutputFormat( mInputDescription );
680 if ( d.exec() == QDialog::Accepted )
682 mExpressionString = d.expressionText().trimmed();
686 mProperty.
setActive( !mExpressionString.isEmpty() );
696void QgsPropertyOverrideButton::showAssistant()
716 mFieldName = this->mProperty.
field();
733 QDialog *dlg =
new QDialog(
this );
734 QString key = QStringLiteral(
"/UI/paneldialog/%1" ).arg( widget->
panelTitle() );
736 dlg->restoreGeometry( settings.
value( key ).toByteArray() );
738 dlg->setLayout(
new QVBoxLayout() );
739 dlg->layout()->addWidget( widget );
740 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
741 connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
742 connect( buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject );
743 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsPropertyOverrideButton::showHelp );
744 dlg->layout()->addWidget( buttonBox );
746 if ( dlg->exec() == QDialog::Accepted )
750 mFieldName = mProperty.
field();
757 settings.
setValue( key, dlg->saveGeometry() );
761void QgsPropertyOverrideButton::updateGui()
763 bool hasExp = !mExpressionString.isEmpty();
764 bool hasField = !mFieldName.isEmpty();
767 QString deftip = tr(
"undefined" );
773 const QString colorName = getColor();
774 if ( !colorName.isEmpty() )
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 QString colorName = getColor();
929 if ( !colorName.isEmpty() )
931 cb->linkToProjectColor( colorName );
936 cb->linkToProjectColor( QString() );
960 mExpressionContextGenerator = generator;
965 for (
const SiblingWidget &sw : std::as_const( mSiblingWidgets ) )
967 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingLinkedWidget )
970 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingLinkedWidget ) );
972 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( widget ) )
984void QgsPropertyOverrideButton::showHelp()
986 QgsHelp::openHelp( QStringLiteral(
"introduction/general_tools.html#data-defined" ) );
989QString QgsPropertyOverrideButton::getColor()
const
991 const thread_local QRegularExpression rx( QStringLiteral(
"^project_color(_object|)\\('(.*)'\\)$" ) );
992 QRegularExpressionMatch match = rx.match( mExpressionString );
993 return match.hasMatch() ? match.captured( 2 ) : QString();
@ 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.