17#include "moc_qgspropertyoverridebutton.cpp"
36#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() )
391 for (
int j = 0; j < mFieldNameList.count(); ++j )
393 QString fldname = mFieldNameList.at( j );
394 QAction *act = mFieldsMenu->addAction( mFieldDisplayNameList.at( j ) );
395 act->setIcon( mFieldIcons.at( j ) );
396 act->setData( QVariant( fldname ) );
397 if ( mFieldName == fldname )
399 act->setCheckable(
true );
407 QAction *act = mFieldsMenu->addAction( tr(
"No matching field types found" ) );
408 act->setEnabled(
false );
411 mDefineMenu->addSeparator();
414 mFieldsMenu->menuAction()->setCheckable(
true );
417 bool colorActive =
false;
418 mColorsMenu->clear();
423 QAction *colorTitleAct = mDefineMenu->addAction( tr(
"Project Color" ) );
424 colorTitleAct->setFont( titlefont );
425 colorTitleAct->setEnabled(
false );
427 QList<QgsProjectColorScheme *> projectSchemes;
429 if ( projectSchemes.length() > 0 )
433 for (
const auto &color : colors )
435 if ( color.second.isEmpty() )
439 QAction *act = mColorsMenu->addAction( color.second );
440 act->setIcon( icon );
443 act->setCheckable(
true );
444 act->setChecked(
true );
450 if ( mColorsMenu->actions().isEmpty() )
452 QAction *act = mColorsMenu->addAction( tr(
"No colors set" ) );
453 act->setEnabled(
false );
456 mDefineMenu->addAction( mActionColors );
457 mColorsMenu->menuAction()->setCheckable(
true );
458 mColorsMenu->menuAction()->setChecked( colorActive && !mProperty.
transformer() );
460 mDefineMenu->addSeparator();
463 QAction *exprTitleAct = mDefineMenu->addAction( tr(
"Expression" ) );
464 exprTitleAct->setFont( titlefont );
465 exprTitleAct->setEnabled(
false );
467 mVariablesMenu->clear();
468 bool variableActive =
false;
469 if ( mExpressionContextGenerator )
474 const auto constVariables = variables;
475 for (
const QString &variable : constVariables )
479 if ( variable.startsWith(
'_' ) )
482 QAction *act = mVariablesMenu->addAction( variable );
483 act->setData( QVariant( variable ) );
487 act->setCheckable(
true );
488 act->setChecked(
true );
489 variableActive =
true;
494 if ( mVariablesMenu->actions().isEmpty() )
496 QAction *act = mVariablesMenu->addAction( tr(
"No variables set" ) );
497 act->setEnabled(
false );
500 mDefineMenu->addAction( mActionVariables );
501 mVariablesMenu->menuAction()->setCheckable(
true );
502 mVariablesMenu->menuAction()->setChecked( variableActive && !mProperty.
transformer() );
506 QString expString = mExpressionString;
507 if ( expString.length() > 35 )
509 expString.truncate( 35 );
510 expString.append( QChar( 0x2026 ) );
513 expString.prepend( tr(
"Current: " ) );
515 if ( !mActionExpression )
517 mActionExpression =
new QAction( expString,
this );
518 mActionExpression->setCheckable(
true );
522 mActionExpression->setText( expString );
524 mDefineMenu->addAction( mActionExpression );
527 mDefineMenu->addAction( mActionExpDialog );
528 mDefineMenu->addAction( mActionCopyExpr );
529 mDefineMenu->addAction( mActionPasteExpr );
533 mDefineMenu->addAction( mActionExpDialog );
534 mDefineMenu->addAction( mActionPasteExpr );
537 if ( hasExp || !mFieldName.isEmpty() )
539 mDefineMenu->addSeparator();
540 mDefineMenu->addAction( mActionClearExpr );
545 mDefineMenu->addSeparator();
546 mActionAssistant->setCheckable( mProperty.
transformer() );
547 mActionAssistant->setChecked( mProperty.
transformer() );
548 mDefineMenu->addAction( mActionAssistant );
552void QgsPropertyOverrideButton::menuActionTriggered( QAction *action )
554 if ( action == mActionActive )
556 setActivePrivate( mActionActive->data().toBool() );
560 else if ( action == mActionDescription )
562 showDescriptionDialog();
564 else if ( action == mActionExpDialog )
566 showExpressionDialog();
568 else if ( action == mActionExpression )
572 setActivePrivate(
true );
577 else if ( action == mActionCopyExpr )
579 QApplication::clipboard()->setText( mExpressionString );
581 else if ( action == mActionPasteExpr )
583 QString exprString = QApplication::clipboard()->text();
584 if ( !exprString.isEmpty() )
586 mExpressionString = exprString;
589 setActivePrivate(
true );
595 else if ( action == mActionClearExpr )
597 setActivePrivate(
false );
600 mExpressionString.clear();
606 else if ( action == mActionAssistant )
610 else if ( action == mActionCreateAuxiliaryField )
614 else if ( mFieldsMenu->actions().contains( action ) )
616 if ( action->isEnabled() )
618 if ( mFieldName != action->text() )
620 mFieldName = action->data().toString();
624 setActivePrivate(
true );
630 else if ( mVariablesMenu->actions().contains( action ) )
632 if ( mExpressionString != action->text().prepend(
"@" ) )
634 mExpressionString = action->data().toString().prepend(
"@" );
638 setActivePrivate(
true );
643 else if ( mColorsMenu->actions().contains( action ) )
645 if ( getColor() != action->text() )
647 mExpressionString = QStringLiteral(
"project_color_object('%1')" ).arg( action->text() );
651 setActivePrivate(
true );
659void QgsPropertyOverrideButton::showDescriptionDialog()
662 mv->setWindowTitle( tr(
"Data Definition Description" ) );
668void QgsPropertyOverrideButton::showExpressionDialog()
677 d.setExpectedOutputFormat( mInputDescription );
678 if ( d.exec() == QDialog::Accepted )
680 mExpressionString = d.expressionText().trimmed();
684 mProperty.
setActive( !mExpressionString.isEmpty() );
694void QgsPropertyOverrideButton::showAssistant()
713 mFieldName = this->mProperty.
field();
730 QDialog *dlg =
new QDialog(
this );
731 QString key = QStringLiteral(
"/UI/paneldialog/%1" ).arg( widget->
panelTitle() );
733 dlg->restoreGeometry( settings.
value( key ).toByteArray() );
735 dlg->setLayout(
new QVBoxLayout() );
736 dlg->layout()->addWidget( widget );
737 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
738 connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
739 connect( buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject );
740 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsPropertyOverrideButton::showHelp );
741 dlg->layout()->addWidget( buttonBox );
743 if ( dlg->exec() == QDialog::Accepted )
747 mFieldName = mProperty.
field();
754 settings.
setValue( key, dlg->saveGeometry() );
758void QgsPropertyOverrideButton::updateGui()
760 bool hasExp = !mExpressionString.isEmpty();
761 bool hasField = !mFieldName.isEmpty();
764 QString deftip = tr(
"undefined" );
770 const QString colorName = getColor();
771 if ( !colorName.isEmpty() )
775 deftype = tr(
"project color" );
780 if ( exp.hasParserError() )
783 deftip = tr(
"Parse error: %1" ).arg( exp.parserErrorString() );
787 deftip = mExpressionString;
795 if ( !mFieldNameList.contains( mFieldName ) && !mProperty.
transformer() )
798 deftip = tr(
"'%1' field missing" ).arg( mFieldName );
809 mFullDescription = tr(
"<b><u>Data defined override</u></b><br>" );
811 mFullDescription += tr(
"<b>Active: </b>%1 <i>(ctrl|right-click toggles)</i><br>" ).arg( mProperty.
isActive() ? tr(
"yes" ) : tr(
"no" ) );
813 if ( !mUsageInfo.isEmpty() )
815 mFullDescription += tr(
"<b>Usage:</b><br>%1<br>" ).arg( mUsageInfo );
818 if ( !mInputDescription.isEmpty() )
820 mFullDescription += tr(
"<b>Expected input:</b><br>%1<br>" ).arg( mInputDescription );
823 if ( !mDataTypesString.isEmpty() )
825 mFullDescription += tr(
"<b>Valid input types:</b><br>%1<br>" ).arg( mDataTypesString );
828 if ( deftype.isEmpty() && deftip != tr(
"undefined" ) )
834 if ( deftip.length() > 75 )
836 deftip.truncate( 75 );
837 deftip.append( QChar( 0x2026 ) );
840 mFullDescription += tr(
"<b>Current definition (%1):</b><br>%2" ).arg( deftype, deftip );
842 setToolTip( mFullDescription );
845void QgsPropertyOverrideButton::setActivePrivate(
bool active )
854void QgsPropertyOverrideButton::updateSiblingWidgets(
bool state )
856 const auto constMSiblingWidgets = mSiblingWidgets;
857 for (
const SiblingWidget &sw : constMSiblingWidgets )
859 switch ( sw.mSiblingType )
861 case SiblingCheckState:
866 QAbstractButton *btn = qobject_cast<QAbstractButton *>( sw.mWidgetPointer.data() );
867 if ( btn && btn->isCheckable() )
869 btn->setChecked( sw.mNatural ? state : !state );
873 QGroupBox *grpbx = qobject_cast<QGroupBox *>( sw.mWidgetPointer.data() );
874 if ( grpbx && grpbx->isCheckable() )
876 grpbx->setChecked( sw.mNatural ? state : !state );
883 case SiblingEnableState:
885 QLineEdit *le = qobject_cast<QLineEdit *>( sw.mWidgetPointer.data() );
887 le->setReadOnly( sw.mNatural ? !state : state );
889 sw.mWidgetPointer.data()->setEnabled( sw.mNatural ? state : !state );
893 case SiblingVisibility:
895 sw.mWidgetPointer.data()->setVisible( sw.mNatural ? state : !state );
899 case SiblingExpressionText:
901 QLineEdit *le = qobject_cast<QLineEdit *>( sw.mWidgetPointer.data() );
908 QTextEdit *te = qobject_cast<QTextEdit *>( sw.mWidgetPointer.data() );
917 case SiblingLinkedWidget:
919 if (
QgsColorButton *cb = qobject_cast<QgsColorButton *>( sw.mWidgetPointer.data() ) )
923 const QString colorName = getColor();
924 if ( !colorName.isEmpty() )
926 cb->linkToProjectColor( colorName );
931 cb->linkToProjectColor( QString() );
954 mExpressionContextGenerator = generator;
959 for (
const SiblingWidget &sw : std::as_const( mSiblingWidgets ) )
961 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingLinkedWidget )
964 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingLinkedWidget ) );
966 if (
QgsColorButton *cb = qobject_cast<QgsColorButton *>( widget ) )
977void QgsPropertyOverrideButton::showHelp()
979 QgsHelp::openHelp( QStringLiteral(
"introduction/general_tools.html#data-defined" ) );
982QString QgsPropertyOverrideButton::getColor()
const
984 const thread_local QRegularExpression rx( QStringLiteral(
"^project_color(_object|)\\('(.*)'\\)$" ) );
985 QRegularExpressionMatch match = rx.match( mExpressionString );
986 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.