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() == QMetaType::Type::QString;
177 fieldMatch = f.type() == QMetaType::Type::QString;
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 if ( event->button() == Qt::MiddleButton )
269 showExpressionDialog();
274 QToolButton::mousePressEvent( event );
288 mFieldName =
property.field();
293 mExpressionString =
property.expressionString();
301 mExpressionString.clear();
303 mProperty = property;
310void QgsPropertyOverrideButton::aboutToShowMenu()
312 mDefineMenu->clear();
316 bool hasExp = !mExpressionString.isEmpty();
317 QString ddTitle = tr(
"Data defined override" );
319 QAction *ddTitleAct = mDefineMenu->addAction( ddTitle );
320 QFont titlefont = ddTitleAct->font();
321 titlefont.setItalic(
true );
322 ddTitleAct->setFont( titlefont );
323 ddTitleAct->setEnabled(
false );
325 bool addActiveAction =
false;
330 addActiveAction = !exp.hasParserError();
335 addActiveAction = mFieldNameList.contains( mFieldName );
338 if ( addActiveAction )
341 mDefineMenu->addAction( mActionActive );
342 mActionActive->setText( mProperty.
isActive() ? tr(
"Deactivate" ) : tr(
"Activate" ) );
343 mActionActive->setData( QVariant( !mProperty.
isActive() ) );
346 if ( !mFullDescription.isEmpty() )
348 mDefineMenu->addAction( mActionDescription );
351 mDefineMenu->addSeparator();
354 if ( mAuxiliaryStorageEnabled && mVectorLayer )
356 mDefineMenu->addAction( mActionCreateAuxiliaryField );
360 mActionCreateAuxiliaryField->setEnabled(
true );
361 mActionCreateAuxiliaryField->setChecked(
false );
365 if ( index >= 0 && alayer && mVectorLayer->
isAuxiliaryField( index, srcIndex ) )
367 mActionCreateAuxiliaryField->setEnabled(
false );
368 mActionCreateAuxiliaryField->setChecked(
true );
372 bool fieldActive =
false;
373 if ( !mDataTypesString.isEmpty() )
375 QAction *fieldTitleAct = mDefineMenu->addAction( tr(
"Attribute Field" ) );
376 fieldTitleAct->setFont( titlefont );
377 fieldTitleAct->setEnabled(
false );
379 mDefineMenu->addAction( mActionDataTypes );
381 mFieldsMenu->clear();
383 if ( !mFieldNameList.isEmpty() )
386 for (
int j = 0; j < mFieldNameList.count(); ++j )
388 QString fldname = mFieldNameList.at( j );
389 QAction *act = mFieldsMenu->addAction( mFieldDisplayNameList.at( j ) );
390 act->setIcon( mFieldIcons.at( j ) );
391 act->setData( QVariant( fldname ) );
392 if ( mFieldName == fldname )
394 act->setCheckable(
true );
402 QAction *act = mFieldsMenu->addAction( tr(
"No matching field types found" ) );
403 act->setEnabled(
false );
406 mDefineMenu->addSeparator();
409 mFieldsMenu->menuAction()->setCheckable(
true );
412 bool colorActive =
false;
413 mColorsMenu->clear();
418 QAction *colorTitleAct = mDefineMenu->addAction( tr(
"Project Color" ) );
419 colorTitleAct->setFont( titlefont );
420 colorTitleAct->setEnabled(
false );
422 QList<QgsProjectColorScheme *> projectSchemes;
424 if ( projectSchemes.length() > 0 )
428 for (
const auto &color : colors )
430 if ( color.second.isEmpty() )
434 QAction *act = mColorsMenu->addAction( color.second );
435 act->setIcon( icon );
438 act->setCheckable(
true );
439 act->setChecked(
true );
445 if ( mColorsMenu->actions().isEmpty() )
447 QAction *act = mColorsMenu->addAction( tr(
"No colors set" ) );
448 act->setEnabled(
false );
451 mDefineMenu->addAction( mActionColors );
452 mColorsMenu->menuAction()->setCheckable(
true );
453 mColorsMenu->menuAction()->setChecked( colorActive && !mProperty.
transformer() );
455 mDefineMenu->addSeparator();
458 QAction *exprTitleAct = mDefineMenu->addAction( tr(
"Expression" ) );
459 exprTitleAct->setFont( titlefont );
460 exprTitleAct->setEnabled(
false );
462 mVariablesMenu->clear();
463 bool variableActive =
false;
464 if ( mExpressionContextGenerator )
469 const auto constVariables = variables;
470 for (
const QString &variable : constVariables )
474 if ( variable.startsWith(
'_' ) )
477 QAction *act = mVariablesMenu->addAction( variable );
478 act->setData( QVariant( variable ) );
482 act->setCheckable(
true );
483 act->setChecked(
true );
484 variableActive =
true;
489 if ( mVariablesMenu->actions().isEmpty() )
491 QAction *act = mVariablesMenu->addAction( tr(
"No variables set" ) );
492 act->setEnabled(
false );
495 mDefineMenu->addAction( mActionVariables );
496 mVariablesMenu->menuAction()->setCheckable(
true );
497 mVariablesMenu->menuAction()->setChecked( variableActive && !mProperty.
transformer() );
501 QString expString = mExpressionString;
502 if ( expString.length() > 35 )
504 expString.truncate( 35 );
505 expString.append( QChar( 0x2026 ) );
508 expString.prepend( tr(
"Current: " ) );
510 if ( !mActionExpression )
512 mActionExpression =
new QAction( expString,
this );
513 mActionExpression->setCheckable(
true );
517 mActionExpression->setText( expString );
519 mDefineMenu->addAction( mActionExpression );
522 mDefineMenu->addAction( mActionExpDialog );
523 mDefineMenu->addAction( mActionCopyExpr );
524 mDefineMenu->addAction( mActionPasteExpr );
528 mDefineMenu->addAction( mActionExpDialog );
529 mDefineMenu->addAction( mActionPasteExpr );
532 if ( hasExp || !mFieldName.isEmpty() )
534 mDefineMenu->addSeparator();
535 mDefineMenu->addAction( mActionClearExpr );
540 mDefineMenu->addSeparator();
541 mActionAssistant->setCheckable( mProperty.
transformer() );
542 mActionAssistant->setChecked( mProperty.
transformer() );
543 mDefineMenu->addAction( mActionAssistant );
547void QgsPropertyOverrideButton::menuActionTriggered( QAction *action )
549 if ( action == mActionActive )
551 setActivePrivate( mActionActive->data().toBool() );
555 else if ( action == mActionDescription )
557 showDescriptionDialog();
559 else if ( action == mActionExpDialog )
561 showExpressionDialog();
563 else if ( action == mActionExpression )
567 setActivePrivate(
true );
572 else if ( action == mActionCopyExpr )
574 QApplication::clipboard()->setText( mExpressionString );
576 else if ( action == mActionPasteExpr )
578 QString exprString = QApplication::clipboard()->text();
579 if ( !exprString.isEmpty() )
581 mExpressionString = exprString;
584 setActivePrivate(
true );
590 else if ( action == mActionClearExpr )
592 setActivePrivate(
false );
595 mExpressionString.clear();
601 else if ( action == mActionAssistant )
605 else if ( action == mActionCreateAuxiliaryField )
609 else if ( mFieldsMenu->actions().contains( action ) )
611 if ( action->isEnabled() )
613 if ( mFieldName != action->text() )
615 mFieldName = action->data().toString();
619 setActivePrivate(
true );
625 else if ( mVariablesMenu->actions().contains( action ) )
627 if ( mExpressionString != action->text().prepend(
"@" ) )
629 mExpressionString = action->data().toString().prepend(
"@" );
633 setActivePrivate(
true );
638 else if ( mColorsMenu->actions().contains( action ) )
640 if ( mExpressionString != QStringLiteral(
"project_color('%1')" ).arg( action->text() ) )
642 mExpressionString = QStringLiteral(
"project_color('%1')" ).arg( action->text() );
646 setActivePrivate(
true );
654void QgsPropertyOverrideButton::showDescriptionDialog()
657 mv->setWindowTitle( tr(
"Data Definition Description" ) );
663void QgsPropertyOverrideButton::showExpressionDialog()
672 d.setExpectedOutputFormat( mInputDescription );
673 if ( d.exec() == QDialog::Accepted )
675 mExpressionString = d.expressionText().trimmed();
679 mProperty.
setActive( !mExpressionString.isEmpty() );
689void QgsPropertyOverrideButton::showAssistant()
709 mFieldName = this->mProperty.
field();
726 QDialog *dlg =
new QDialog(
this );
727 QString key = QStringLiteral(
"/UI/paneldialog/%1" ).arg( widget->
panelTitle() );
729 dlg->restoreGeometry( settings.
value( key ).toByteArray() );
731 dlg->setLayout(
new QVBoxLayout() );
732 dlg->layout()->addWidget( widget );
733 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
734 connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
735 connect( buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject );
736 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsPropertyOverrideButton::showHelp );
737 dlg->layout()->addWidget( buttonBox );
739 if ( dlg->exec() == QDialog::Accepted )
743 mFieldName = mProperty.
field();
750 settings.
setValue( key, dlg->saveGeometry() );
754void QgsPropertyOverrideButton::updateGui()
756 bool hasExp = !mExpressionString.isEmpty();
757 bool hasField = !mFieldName.isEmpty();
760 QString deftip = tr(
"undefined" );
766 const thread_local QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
767 QRegularExpressionMatch match = rx.match( mExpressionString );
768 if ( match.hasMatch() )
771 deftip = match.captured( 1 );
772 deftype = tr(
"project color" );
777 if ( exp.hasParserError() )
780 deftip = tr(
"Parse error: %1" ).arg( exp.parserErrorString() );
784 deftip = mExpressionString;
792 if ( !mFieldNameList.contains( mFieldName ) && !mProperty.
transformer() )
795 deftip = tr(
"'%1' field missing" ).arg( mFieldName );
806 mFullDescription = tr(
"<b><u>Data defined override</u></b><br>" );
808 mFullDescription += tr(
"<b>Active: </b>%1 <i>(ctrl|right-click toggles)</i><br>" ).arg( mProperty.
isActive() ? tr(
"yes" ) : tr(
"no" ) );
810 if ( !mUsageInfo.isEmpty() )
812 mFullDescription += tr(
"<b>Usage:</b><br>%1<br>" ).arg( mUsageInfo );
815 if ( !mInputDescription.isEmpty() )
817 mFullDescription += tr(
"<b>Expected input:</b><br>%1<br>" ).arg( mInputDescription );
820 if ( !mDataTypesString.isEmpty() )
822 mFullDescription += tr(
"<b>Valid input types:</b><br>%1<br>" ).arg( mDataTypesString );
825 if ( deftype.isEmpty() && deftip != tr(
"undefined" ) )
831 if ( deftip.length() > 75 )
833 deftip.truncate( 75 );
834 deftip.append( QChar( 0x2026 ) );
837 mFullDescription += tr(
"<b>Current definition (%1):</b><br>%2" ).arg( deftype, deftip );
839 setToolTip( mFullDescription );
843void QgsPropertyOverrideButton::setActivePrivate(
bool active )
852void QgsPropertyOverrideButton::updateSiblingWidgets(
bool state )
854 const auto constMSiblingWidgets = mSiblingWidgets;
855 for (
const SiblingWidget &sw : constMSiblingWidgets )
857 switch ( sw.mSiblingType )
860 case SiblingCheckState:
865 QAbstractButton *btn = qobject_cast< QAbstractButton * >( sw.mWidgetPointer.data() );
866 if ( btn && btn->isCheckable() )
868 btn->setChecked( sw.mNatural ? state : !state );
872 QGroupBox *grpbx = qobject_cast< QGroupBox * >( sw.mWidgetPointer.data() );
873 if ( grpbx && grpbx->isCheckable() )
875 grpbx->setChecked( sw.mNatural ? state : !state );
882 case SiblingEnableState:
884 QLineEdit *le = qobject_cast< QLineEdit * >( sw.mWidgetPointer.data() );
886 le->setReadOnly( sw.mNatural ? !state : state );
888 sw.mWidgetPointer.data()->setEnabled( sw.mNatural ? state : !state );
892 case SiblingVisibility:
894 sw.mWidgetPointer.data()->setVisible( sw.mNatural ? state : !state );
898 case SiblingExpressionText:
900 QLineEdit *le = qobject_cast<QLineEdit *>( sw.mWidgetPointer.data() );
907 QTextEdit *te = qobject_cast<QTextEdit *>( sw.mWidgetPointer.data() );
916 case SiblingLinkedWidget:
918 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( sw.mWidgetPointer.data() ) )
922 const thread_local QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
923 QRegularExpressionMatch match = rx.match( mExpressionString );
924 if ( match.hasMatch() )
926 cb->linkToProjectColor( match.captured( 1 ) );
931 cb->linkToProjectColor( QString() );
955 mExpressionContextGenerator = generator;
960 for (
const SiblingWidget &sw : std::as_const( mSiblingWidgets ) )
962 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingLinkedWidget )
965 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingLinkedWidget ) );
967 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( widget ) )
979void QgsPropertyOverrideButton::showHelp()
981 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.