32 #include <QMouseEvent>
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 ) );
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() == QVariant::String;
177 fieldMatch = f.type() == QVariant::String;
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 QToolButton::mousePressEvent( event );
281 mFieldName =
property.field();
286 mExpressionString =
property.expressionString();
294 mExpressionString.clear();
296 mProperty = property;
303 void QgsPropertyOverrideButton::aboutToShowMenu()
305 mDefineMenu->clear();
309 bool hasExp = !mExpressionString.isEmpty();
310 QString ddTitle = tr(
"Data defined override" );
312 QAction *ddTitleAct = mDefineMenu->addAction( ddTitle );
313 QFont titlefont = ddTitleAct->font();
314 titlefont.setItalic(
true );
315 ddTitleAct->setFont( titlefont );
316 ddTitleAct->setEnabled(
false );
318 bool addActiveAction =
false;
323 addActiveAction = !exp.hasParserError();
328 addActiveAction = mFieldNameList.contains( mFieldName );
331 if ( addActiveAction )
334 mDefineMenu->addAction( mActionActive );
335 mActionActive->setText( mProperty.
isActive() ? tr(
"Deactivate" ) : tr(
"Activate" ) );
336 mActionActive->setData( QVariant( !mProperty.
isActive() ) );
339 if ( !mFullDescription.isEmpty() )
341 mDefineMenu->addAction( mActionDescription );
344 mDefineMenu->addSeparator();
347 if ( mAuxiliaryStorageEnabled && mVectorLayer )
349 mDefineMenu->addAction( mActionCreateAuxiliaryField );
353 mActionCreateAuxiliaryField->setEnabled(
true );
354 mActionCreateAuxiliaryField->setChecked(
false );
358 if ( index >= 0 && alayer && mVectorLayer->
isAuxiliaryField( index, srcIndex ) )
360 mActionCreateAuxiliaryField->setEnabled(
false );
361 mActionCreateAuxiliaryField->setChecked(
true );
365 bool fieldActive =
false;
366 if ( !mDataTypesString.isEmpty() )
368 QAction *fieldTitleAct = mDefineMenu->addAction( tr(
"Attribute Field" ) );
369 fieldTitleAct->setFont( titlefont );
370 fieldTitleAct->setEnabled(
false );
372 mDefineMenu->addAction( mActionDataTypes );
374 mFieldsMenu->clear();
376 if ( !mFieldNameList.isEmpty() )
379 for (
int j = 0; j < mFieldNameList.count(); ++j )
381 QString fldname = mFieldNameList.at( j );
382 QAction *act = mFieldsMenu->addAction( mFieldDisplayNameList.at( j ) );
383 act->setIcon( mFieldIcons.at( j ) );
384 act->setData( QVariant( fldname ) );
385 if ( mFieldName == fldname )
387 act->setCheckable(
true );
395 QAction *act = mFieldsMenu->addAction( tr(
"No matching field types found" ) );
396 act->setEnabled(
false );
399 mDefineMenu->addSeparator();
402 mFieldsMenu->menuAction()->setCheckable(
true );
405 bool colorActive =
false;
406 mColorsMenu->clear();
411 QAction *colorTitleAct = mDefineMenu->addAction( tr(
"Project Color" ) );
412 colorTitleAct->setFont( titlefont );
413 colorTitleAct->setEnabled(
false );
415 QList<QgsProjectColorScheme *> projectSchemes;
417 if ( projectSchemes.length() > 0 )
421 for (
const auto &color : colors )
423 if ( color.second.isEmpty() )
427 QAction *act = mColorsMenu->addAction( color.second );
428 act->setIcon( icon );
431 act->setCheckable(
true );
432 act->setChecked(
true );
438 if ( mColorsMenu->actions().isEmpty() )
440 QAction *act = mColorsMenu->addAction( tr(
"No colors set" ) );
441 act->setEnabled(
false );
444 mDefineMenu->addAction( mActionColors );
445 mColorsMenu->menuAction()->setCheckable(
true );
446 mColorsMenu->menuAction()->setChecked( colorActive && !mProperty.
transformer() );
448 mDefineMenu->addSeparator();
451 QAction *exprTitleAct = mDefineMenu->addAction( tr(
"Expression" ) );
452 exprTitleAct->setFont( titlefont );
453 exprTitleAct->setEnabled(
false );
455 mVariablesMenu->clear();
456 bool variableActive =
false;
457 if ( mExpressionContextGenerator )
461 const auto constVariables = variables;
462 for (
const QString &variable : constVariables )
466 if ( variable.startsWith(
'_' ) )
469 QAction *act = mVariablesMenu->addAction( variable );
470 act->setData( QVariant( variable ) );
474 act->setCheckable(
true );
475 act->setChecked(
true );
476 variableActive =
true;
481 if ( mVariablesMenu->actions().isEmpty() )
483 QAction *act = mVariablesMenu->addAction( tr(
"No variables set" ) );
484 act->setEnabled(
false );
487 mDefineMenu->addAction( mActionVariables );
488 mVariablesMenu->menuAction()->setCheckable(
true );
489 mVariablesMenu->menuAction()->setChecked( variableActive && !mProperty.
transformer() );
493 QString expString = mExpressionString;
494 if ( expString.length() > 35 )
496 expString.truncate( 35 );
497 expString.append( QChar( 0x2026 ) );
500 expString.prepend( tr(
"Current: " ) );
502 if ( !mActionExpression )
504 mActionExpression =
new QAction( expString,
this );
505 mActionExpression->setCheckable(
true );
509 mActionExpression->setText( expString );
511 mDefineMenu->addAction( mActionExpression );
514 mDefineMenu->addAction( mActionExpDialog );
515 mDefineMenu->addAction( mActionCopyExpr );
516 mDefineMenu->addAction( mActionPasteExpr );
520 mDefineMenu->addAction( mActionExpDialog );
521 mDefineMenu->addAction( mActionPasteExpr );
524 if ( hasExp || !mFieldName.isEmpty() )
526 mDefineMenu->addSeparator();
527 mDefineMenu->addAction( mActionClearExpr );
532 mDefineMenu->addSeparator();
533 mActionAssistant->setCheckable( mProperty.
transformer() );
534 mActionAssistant->setChecked( mProperty.
transformer() );
535 mDefineMenu->addAction( mActionAssistant );
539 void QgsPropertyOverrideButton::menuActionTriggered( QAction *action )
541 if ( action == mActionActive )
543 setActivePrivate( mActionActive->data().toBool() );
547 else if ( action == mActionDescription )
549 showDescriptionDialog();
551 else if ( action == mActionExpDialog )
553 showExpressionDialog();
555 else if ( action == mActionExpression )
559 setActivePrivate(
true );
564 else if ( action == mActionCopyExpr )
566 QApplication::clipboard()->setText( mExpressionString );
568 else if ( action == mActionPasteExpr )
570 QString exprString = QApplication::clipboard()->text();
571 if ( !exprString.isEmpty() )
573 mExpressionString = exprString;
576 setActivePrivate(
true );
582 else if ( action == mActionClearExpr )
584 setActivePrivate(
false );
587 mExpressionString.clear();
593 else if ( action == mActionAssistant )
597 else if ( action == mActionCreateAuxiliaryField )
601 else if ( mFieldsMenu->actions().contains( action ) )
603 if ( action->isEnabled() )
605 if ( mFieldName != action->text() )
607 mFieldName = action->data().toString();
611 setActivePrivate(
true );
617 else if ( mVariablesMenu->actions().contains( action ) )
619 if ( mExpressionString != action->text().prepend(
"@" ) )
621 mExpressionString = action->data().toString().prepend(
"@" );
625 setActivePrivate(
true );
630 else if ( mColorsMenu->actions().contains( action ) )
632 if ( mExpressionString != QStringLiteral(
"project_color('%1')" ).arg( action->text() ) )
634 mExpressionString = QStringLiteral(
"project_color('%1')" ).arg( action->text() );
638 setActivePrivate(
true );
646 void QgsPropertyOverrideButton::showDescriptionDialog()
649 mv->setWindowTitle( tr(
"Data Definition Description" ) );
655 void QgsPropertyOverrideButton::showExpressionDialog()
664 d.setExpectedOutputFormat( mInputDescription );
665 if ( d.exec() == QDialog::Accepted )
667 mExpressionString = d.expressionText().trimmed();
671 mProperty.
setActive( !mExpressionString.isEmpty() );
681 void QgsPropertyOverrideButton::showAssistant()
701 mFieldName = this->mProperty.
field();
718 QDialog *dlg =
new QDialog(
this );
719 QString key = QStringLiteral(
"/UI/paneldialog/%1" ).arg( widget->
panelTitle() );
721 dlg->restoreGeometry( settings.
value( key ).toByteArray() );
723 dlg->setLayout(
new QVBoxLayout() );
724 dlg->layout()->addWidget( widget );
725 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
726 connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
727 connect( buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject );
728 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsPropertyOverrideButton::showHelp );
729 dlg->layout()->addWidget( buttonBox );
731 if ( dlg->exec() == QDialog::Accepted )
735 mFieldName = mProperty.
field();
742 settings.
setValue( key, dlg->saveGeometry() );
746 void QgsPropertyOverrideButton::updateGui()
748 bool hasExp = !mExpressionString.isEmpty();
749 bool hasField = !mFieldName.isEmpty();
752 QString deftip = tr(
"undefined" );
758 QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
759 QRegularExpressionMatch match = rx.match( mExpressionString );
760 if ( match.hasMatch() )
763 deftip = match.captured( 1 );
764 deftype = tr(
"project color" );
769 if ( exp.hasParserError() )
772 deftip = tr(
"Parse error: %1" ).arg( exp.parserErrorString() );
776 deftip = mExpressionString;
784 if ( !mFieldNameList.contains( mFieldName ) && !mProperty.
transformer() )
787 deftip = tr(
"'%1' field missing" ).arg( mFieldName );
798 mFullDescription = tr(
"<b><u>Data defined override</u></b><br>" );
800 mFullDescription += tr(
"<b>Active: </b>%1 <i>(ctrl|right-click toggles)</i><br>" ).arg( mProperty.
isActive() ? tr(
"yes" ) : tr(
"no" ) );
802 if ( !mUsageInfo.isEmpty() )
804 mFullDescription += tr(
"<b>Usage:</b><br>%1<br>" ).arg( mUsageInfo );
807 if ( !mInputDescription.isEmpty() )
809 mFullDescription += tr(
"<b>Expected input:</b><br>%1<br>" ).arg( mInputDescription );
812 if ( !mDataTypesString.isEmpty() )
814 mFullDescription += tr(
"<b>Valid input types:</b><br>%1<br>" ).arg( mDataTypesString );
817 if ( deftype.isEmpty() && deftip != tr(
"undefined" ) )
823 if ( deftip.length() > 75 )
825 deftip.truncate( 75 );
826 deftip.append( QChar( 0x2026 ) );
829 mFullDescription += tr(
"<b>Current definition (%1):</b><br>%2" ).arg( deftype, deftip );
831 setToolTip( mFullDescription );
835 void QgsPropertyOverrideButton::setActivePrivate(
bool active )
844 void QgsPropertyOverrideButton::updateSiblingWidgets(
bool state )
846 const auto constMSiblingWidgets = mSiblingWidgets;
847 for (
const SiblingWidget &sw : constMSiblingWidgets )
849 switch ( sw.mSiblingType )
852 case SiblingCheckState:
857 QAbstractButton *btn = qobject_cast< QAbstractButton * >( sw.mWidgetPointer.data() );
858 if ( btn && btn->isCheckable() )
860 btn->setChecked( sw.mNatural ? state : !state );
864 QGroupBox *grpbx = qobject_cast< QGroupBox * >( sw.mWidgetPointer.data() );
865 if ( grpbx && grpbx->isCheckable() )
867 grpbx->setChecked( sw.mNatural ? state : !state );
874 case SiblingEnableState:
876 QLineEdit *le = qobject_cast< QLineEdit * >( sw.mWidgetPointer.data() );
878 le->setReadOnly( sw.mNatural ? !state : state );
880 sw.mWidgetPointer.data()->setEnabled( sw.mNatural ? state : !state );
884 case SiblingVisibility:
886 sw.mWidgetPointer.data()->setVisible( sw.mNatural ? state : !state );
890 case SiblingExpressionText:
892 QLineEdit *le = qobject_cast<QLineEdit *>( sw.mWidgetPointer.data() );
899 QTextEdit *te = qobject_cast<QTextEdit *>( sw.mWidgetPointer.data() );
908 case SiblingLinkedWidget:
910 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( sw.mWidgetPointer.data() ) )
914 QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
915 QRegularExpressionMatch match = rx.match( mExpressionString );
916 if ( match.hasMatch() )
918 cb->linkToProjectColor( match.captured( 1 ) );
923 cb->linkToProjectColor( QString() );
947 mExpressionContextGenerator = generator;
952 for (
const SiblingWidget &sw : std::as_const( mSiblingWidgets ) )
954 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingLinkedWidget )
957 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingLinkedWidget ) );
959 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( widget ) )
971 void QgsPropertyOverrideButton::showHelp()
973 QgsHelp::openHelp( QStringLiteral(
"introduction/general_tools.html#data-defined" ) );
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.
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.
@ ExpressionBasedProperty
Expression based property (QgsExpressionBasedProperty)
@ StaticProperty
Static property (QgsStaticProperty)
@ FieldBasedProperty
Field based property (QgsFieldBasedProperty)
@ InvalidProperty
Invalid (not set) property.
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.
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.
Type propertyType() const
Returns the property type.
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.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
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.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
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.