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 ( getColor() != action->text() )
648 mExpressionString = QStringLiteral(
"project_color_object('%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 QString colorName = getColor();
773 if ( !colorName.isEmpty() )
777 deftype = tr(
"project color" );
782 if ( exp.hasParserError() )
785 deftip = tr(
"Parse error: %1" ).arg( exp.parserErrorString() );
789 deftip = mExpressionString;
797 if ( !mFieldNameList.contains( mFieldName ) && !mProperty.
transformer() )
800 deftip = tr(
"'%1' field missing" ).arg( mFieldName );
811 mFullDescription = tr(
"<b><u>Data defined override</u></b><br>" );
813 mFullDescription += tr(
"<b>Active: </b>%1 <i>(ctrl|right-click toggles)</i><br>" ).arg( mProperty.
isActive() ? tr(
"yes" ) : tr(
"no" ) );
815 if ( !mUsageInfo.isEmpty() )
817 mFullDescription += tr(
"<b>Usage:</b><br>%1<br>" ).arg( mUsageInfo );
820 if ( !mInputDescription.isEmpty() )
822 mFullDescription += tr(
"<b>Expected input:</b><br>%1<br>" ).arg( mInputDescription );
825 if ( !mDataTypesString.isEmpty() )
827 mFullDescription += tr(
"<b>Valid input types:</b><br>%1<br>" ).arg( mDataTypesString );
830 if ( deftype.isEmpty() && deftip != tr(
"undefined" ) )
836 if ( deftip.length() > 75 )
838 deftip.truncate( 75 );
839 deftip.append( QChar( 0x2026 ) );
842 mFullDescription += tr(
"<b>Current definition (%1):</b><br>%2" ).arg( deftype, deftip );
844 setToolTip( mFullDescription );
848void QgsPropertyOverrideButton::setActivePrivate(
bool active )
857void QgsPropertyOverrideButton::updateSiblingWidgets(
bool state )
859 const auto constMSiblingWidgets = mSiblingWidgets;
860 for (
const SiblingWidget &sw : constMSiblingWidgets )
862 switch ( sw.mSiblingType )
865 case SiblingCheckState:
870 QAbstractButton *btn = qobject_cast< QAbstractButton * >( sw.mWidgetPointer.data() );
871 if ( btn && btn->isCheckable() )
873 btn->setChecked( sw.mNatural ? state : !state );
877 QGroupBox *grpbx = qobject_cast< QGroupBox * >( sw.mWidgetPointer.data() );
878 if ( grpbx && grpbx->isCheckable() )
880 grpbx->setChecked( sw.mNatural ? state : !state );
887 case SiblingEnableState:
889 QLineEdit *le = qobject_cast< QLineEdit * >( sw.mWidgetPointer.data() );
891 le->setReadOnly( sw.mNatural ? !state : state );
893 sw.mWidgetPointer.data()->setEnabled( sw.mNatural ? state : !state );
897 case SiblingVisibility:
899 sw.mWidgetPointer.data()->setVisible( sw.mNatural ? state : !state );
903 case SiblingExpressionText:
905 QLineEdit *le = qobject_cast<QLineEdit *>( sw.mWidgetPointer.data() );
912 QTextEdit *te = qobject_cast<QTextEdit *>( sw.mWidgetPointer.data() );
921 case SiblingLinkedWidget:
923 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( sw.mWidgetPointer.data() ) )
927 const QString colorName = getColor();
928 if ( !colorName.isEmpty() )
930 cb->linkToProjectColor( colorName );
935 cb->linkToProjectColor( QString() );
959 mExpressionContextGenerator = generator;
964 for (
const SiblingWidget &sw : std::as_const( mSiblingWidgets ) )
966 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingLinkedWidget )
969 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingLinkedWidget ) );
971 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( widget ) )
983void QgsPropertyOverrideButton::showHelp()
985 QgsHelp::openHelp( QStringLiteral(
"introduction/general_tools.html#data-defined" ) );
988QString QgsPropertyOverrideButton::getColor()
const
990 const thread_local QRegularExpression rx( QStringLiteral(
"^project_color(_object|)\\('(.*)'\\)$" ) );
991 QRegularExpressionMatch match = rx.match( mExpressionString );
992 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.