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;
204 const auto constMSiblingWidgets = mSiblingWidgets;
205 for (
const SiblingWidget &sw : constMSiblingWidgets )
207 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingCheckState )
210 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingCheckState, natural ) );
216 const auto constMSiblingWidgets = mSiblingWidgets;
217 for (
const SiblingWidget &sw : constMSiblingWidgets )
219 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingEnableState )
222 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingEnableState, natural ) );
228 const auto constMSiblingWidgets = mSiblingWidgets;
229 for (
const SiblingWidget &sw : constMSiblingWidgets )
231 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingVisibility )
234 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingVisibility, natural ) );
240 const auto constMSiblingWidgets = mSiblingWidgets;
241 for (
const SiblingWidget &sw : constMSiblingWidgets )
243 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingExpressionText )
246 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingExpressionText ) );
254 if ( ( event->modifiers() & ( Qt::ControlModifier ) )
255 || event->button() == Qt::RightButton )
257 setActivePrivate( !mProperty.
isActive() );
265 QToolButton::mousePressEvent( event );
279 mFieldName =
property.field();
284 mExpressionString =
property.expressionString();
292 mExpressionString.clear();
294 mProperty = property;
301 void QgsPropertyOverrideButton::aboutToShowMenu()
303 mDefineMenu->clear();
307 bool hasExp = !mExpressionString.isEmpty();
308 QString ddTitle = tr(
"Data defined override" );
310 QAction *ddTitleAct = mDefineMenu->addAction( ddTitle );
311 QFont titlefont = ddTitleAct->font();
312 titlefont.setItalic(
true );
313 ddTitleAct->setFont( titlefont );
314 ddTitleAct->setEnabled(
false );
316 bool addActiveAction =
false;
321 addActiveAction = !exp.hasParserError();
326 addActiveAction = mFieldNameList.contains( mFieldName );
329 if ( addActiveAction )
332 mDefineMenu->addAction( mActionActive );
333 mActionActive->setText( mProperty.
isActive() ? tr(
"Deactivate" ) : tr(
"Activate" ) );
334 mActionActive->setData( QVariant( !mProperty.
isActive() ) );
337 if ( !mFullDescription.isEmpty() )
339 mDefineMenu->addAction( mActionDescription );
342 mDefineMenu->addSeparator();
345 if ( mAuxiliaryStorageEnabled && mVectorLayer )
347 mDefineMenu->addAction( mActionCreateAuxiliaryField );
351 mActionCreateAuxiliaryField->setEnabled(
true );
352 mActionCreateAuxiliaryField->setChecked(
false );
356 if ( index >= 0 && alayer && mVectorLayer->
isAuxiliaryField( index, srcIndex ) )
358 mActionCreateAuxiliaryField->setEnabled(
false );
359 mActionCreateAuxiliaryField->setChecked(
true );
363 bool fieldActive =
false;
364 if ( !mDataTypesString.isEmpty() )
366 QAction *fieldTitleAct = mDefineMenu->addAction( tr(
"Attribute Field" ) );
367 fieldTitleAct->setFont( titlefont );
368 fieldTitleAct->setEnabled(
false );
370 mDefineMenu->addAction( mActionDataTypes );
372 mFieldsMenu->clear();
374 if ( !mFieldNameList.isEmpty() )
377 for (
int j = 0; j < mFieldNameList.count(); ++j )
379 QString fldname = mFieldNameList.at( j );
380 QAction *act = mFieldsMenu->addAction( mFieldDisplayNameList.at( j ) );
381 act->setIcon( mFieldIcons.at( j ) );
382 act->setData( QVariant( fldname ) );
383 if ( mFieldName == fldname )
385 act->setCheckable(
true );
393 QAction *act = mFieldsMenu->addAction( tr(
"No matching field types found" ) );
394 act->setEnabled(
false );
397 mDefineMenu->addSeparator();
400 mFieldsMenu->menuAction()->setCheckable(
true );
403 bool colorActive =
false;
404 mColorsMenu->clear();
409 QAction *colorTitleAct = mDefineMenu->addAction( tr(
"Project Color" ) );
410 colorTitleAct->setFont( titlefont );
411 colorTitleAct->setEnabled(
false );
413 QList<QgsProjectColorScheme *> projectSchemes;
415 if ( projectSchemes.length() > 0 )
419 for (
const auto &color : colors )
421 if ( color.second.isEmpty() )
425 QAction *act = mColorsMenu->addAction( color.second );
426 act->setIcon( icon );
429 act->setCheckable(
true );
430 act->setChecked(
true );
436 if ( mColorsMenu->actions().isEmpty() )
438 QAction *act = mColorsMenu->addAction( tr(
"No colors set" ) );
439 act->setEnabled(
false );
442 mDefineMenu->addAction( mActionColors );
443 mColorsMenu->menuAction()->setCheckable(
true );
444 mColorsMenu->menuAction()->setChecked( colorActive && !mProperty.
transformer() );
446 mDefineMenu->addSeparator();
449 QAction *exprTitleAct = mDefineMenu->addAction( tr(
"Expression" ) );
450 exprTitleAct->setFont( titlefont );
451 exprTitleAct->setEnabled(
false );
453 mVariablesMenu->clear();
454 bool variableActive =
false;
455 if ( mExpressionContextGenerator )
459 const auto constVariables = variables;
460 for (
const QString &variable : constVariables )
464 if ( variable.startsWith(
'_' ) )
467 QAction *act = mVariablesMenu->addAction( variable );
468 act->setData( QVariant( variable ) );
472 act->setCheckable(
true );
473 act->setChecked(
true );
474 variableActive =
true;
479 if ( mVariablesMenu->actions().isEmpty() )
481 QAction *act = mVariablesMenu->addAction( tr(
"No variables set" ) );
482 act->setEnabled(
false );
485 mDefineMenu->addAction( mActionVariables );
486 mVariablesMenu->menuAction()->setCheckable(
true );
487 mVariablesMenu->menuAction()->setChecked( variableActive && !mProperty.
transformer() );
491 QString expString = mExpressionString;
492 if ( expString.length() > 35 )
494 expString.truncate( 35 );
495 expString.append( QChar( 0x2026 ) );
498 expString.prepend( tr(
"Current: " ) );
500 if ( !mActionExpression )
502 mActionExpression =
new QAction( expString,
this );
503 mActionExpression->setCheckable(
true );
507 mActionExpression->setText( expString );
509 mDefineMenu->addAction( mActionExpression );
512 mDefineMenu->addAction( mActionExpDialog );
513 mDefineMenu->addAction( mActionCopyExpr );
514 mDefineMenu->addAction( mActionPasteExpr );
518 mDefineMenu->addAction( mActionExpDialog );
519 mDefineMenu->addAction( mActionPasteExpr );
522 if ( hasExp || !mFieldName.isEmpty() )
524 mDefineMenu->addSeparator();
525 mDefineMenu->addAction( mActionClearExpr );
530 mDefineMenu->addSeparator();
531 mActionAssistant->setCheckable( mProperty.
transformer() );
532 mActionAssistant->setChecked( mProperty.
transformer() );
533 mDefineMenu->addAction( mActionAssistant );
537 void QgsPropertyOverrideButton::menuActionTriggered( QAction *action )
539 if ( action == mActionActive )
541 setActivePrivate( mActionActive->data().toBool() );
545 else if ( action == mActionDescription )
547 showDescriptionDialog();
549 else if ( action == mActionExpDialog )
551 showExpressionDialog();
553 else if ( action == mActionExpression )
557 setActivePrivate(
true );
562 else if ( action == mActionCopyExpr )
564 QApplication::clipboard()->setText( mExpressionString );
566 else if ( action == mActionPasteExpr )
568 QString exprString = QApplication::clipboard()->text();
569 if ( !exprString.isEmpty() )
571 mExpressionString = exprString;
574 setActivePrivate(
true );
580 else if ( action == mActionClearExpr )
582 setActivePrivate(
false );
585 mExpressionString.clear();
591 else if ( action == mActionAssistant )
595 else if ( action == mActionCreateAuxiliaryField )
599 else if ( mFieldsMenu->actions().contains( action ) )
601 if ( action->isEnabled() )
603 if ( mFieldName != action->text() )
605 mFieldName = action->data().toString();
609 setActivePrivate(
true );
615 else if ( mVariablesMenu->actions().contains( action ) )
617 if ( mExpressionString != action->text().prepend(
"@" ) )
619 mExpressionString = action->data().toString().prepend(
"@" );
623 setActivePrivate(
true );
628 else if ( mColorsMenu->actions().contains( action ) )
630 if ( mExpressionString != QStringLiteral(
"project_color('%1')" ).arg( action->text() ) )
632 mExpressionString = QStringLiteral(
"project_color('%1')" ).arg( action->text() );
636 setActivePrivate(
true );
644 void QgsPropertyOverrideButton::showDescriptionDialog()
647 mv->setWindowTitle( tr(
"Data Definition Description" ) );
653 void QgsPropertyOverrideButton::showExpressionDialog()
662 d.setExpectedOutputFormat( mInputDescription );
663 if ( d.exec() == QDialog::Accepted )
665 mExpressionString = d.expressionText().trimmed();
669 mProperty.
setActive( !mExpressionString.isEmpty() );
679 void QgsPropertyOverrideButton::showAssistant()
699 mFieldName = this->mProperty.
field();
716 QDialog *dlg =
new QDialog(
this );
717 QString key = QStringLiteral(
"/UI/paneldialog/%1" ).arg( widget->
panelTitle() );
719 dlg->restoreGeometry( settings.
value( key ).toByteArray() );
721 dlg->setLayout(
new QVBoxLayout() );
722 dlg->layout()->addWidget( widget );
723 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
724 connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
725 connect( buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject );
726 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsPropertyOverrideButton::showHelp );
727 dlg->layout()->addWidget( buttonBox );
729 if ( dlg->exec() == QDialog::Accepted )
733 mFieldName = mProperty.
field();
740 settings.
setValue( key, dlg->saveGeometry() );
744 void QgsPropertyOverrideButton::updateGui()
746 bool hasExp = !mExpressionString.isEmpty();
747 bool hasField = !mFieldName.isEmpty();
750 QString deftip = tr(
"undefined" );
756 QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
757 QRegularExpressionMatch match = rx.match( mExpressionString );
758 if ( match.hasMatch() )
761 deftip = match.captured( 1 );
762 deftype = tr(
"project color" );
767 if ( exp.hasParserError() )
770 deftip = tr(
"Parse error: %1" ).arg( exp.parserErrorString() );
774 deftip = mExpressionString;
782 if ( !mFieldNameList.contains( mFieldName ) && !mProperty.
transformer() )
785 deftip = tr(
"'%1' field missing" ).arg( mFieldName );
796 mFullDescription = tr(
"<b><u>Data defined override</u></b><br>" );
798 mFullDescription += tr(
"<b>Active: </b>%1 <i>(ctrl|right-click toggles)</i><br>" ).arg( mProperty.
isActive() ? tr(
"yes" ) : tr(
"no" ) );
800 if ( !mUsageInfo.isEmpty() )
802 mFullDescription += tr(
"<b>Usage:</b><br>%1<br>" ).arg( mUsageInfo );
805 if ( !mInputDescription.isEmpty() )
807 mFullDescription += tr(
"<b>Expected input:</b><br>%1<br>" ).arg( mInputDescription );
810 if ( !mDataTypesString.isEmpty() )
812 mFullDescription += tr(
"<b>Valid input types:</b><br>%1<br>" ).arg( mDataTypesString );
815 if ( deftype.isEmpty() && deftip != tr(
"undefined" ) )
821 if ( deftip.length() > 75 )
823 deftip.truncate( 75 );
824 deftip.append( QChar( 0x2026 ) );
827 mFullDescription += tr(
"<b>Current definition (%1):</b><br>%2" ).arg( deftype, deftip );
829 setToolTip( mFullDescription );
833 void QgsPropertyOverrideButton::setActivePrivate(
bool active )
842 void QgsPropertyOverrideButton::updateSiblingWidgets(
bool state )
844 const auto constMSiblingWidgets = mSiblingWidgets;
845 for (
const SiblingWidget &sw : constMSiblingWidgets )
847 switch ( sw.mSiblingType )
850 case SiblingCheckState:
855 QAbstractButton *btn = qobject_cast< QAbstractButton * >( sw.mWidgetPointer.data() );
856 if ( btn && btn->isCheckable() )
858 btn->setChecked( sw.mNatural ? state : !state );
862 QGroupBox *grpbx = qobject_cast< QGroupBox * >( sw.mWidgetPointer.data() );
863 if ( grpbx && grpbx->isCheckable() )
865 grpbx->setChecked( sw.mNatural ? state : !state );
872 case SiblingEnableState:
874 QLineEdit *le = qobject_cast< QLineEdit * >( sw.mWidgetPointer.data() );
876 le->setReadOnly( sw.mNatural ? !state : state );
878 sw.mWidgetPointer.data()->setEnabled( sw.mNatural ? state : !state );
882 case SiblingVisibility:
884 sw.mWidgetPointer.data()->setVisible( sw.mNatural ? state : !state );
888 case SiblingExpressionText:
890 QLineEdit *le = qobject_cast<QLineEdit *>( sw.mWidgetPointer.data() );
897 QTextEdit *te = qobject_cast<QTextEdit *>( sw.mWidgetPointer.data() );
906 case SiblingLinkedWidget:
908 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( sw.mWidgetPointer.data() ) )
912 QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
913 QRegularExpressionMatch match = rx.match( mExpressionString );
914 if ( match.hasMatch() )
916 cb->linkToProjectColor( match.captured( 1 ) );
921 cb->linkToProjectColor( QString() );
945 mExpressionContextGenerator = generator;
950 for (
const SiblingWidget &sw : std::as_const( mSiblingWidgets ) )
952 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingLinkedWidget )
955 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingLinkedWidget ) );
957 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( widget ) )
969 void QgsPropertyOverrideButton::showHelp()
971 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.