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 mFieldTypeList.clear();
163 bool fieldMatch =
false;
166 switch ( mDataTypes )
173 fieldMatch = f.isNumeric() || f.type() == QVariant::String;
177 fieldMatch = f.type() == QVariant::String;
183 case QVariant::String:
184 fieldType = tr(
"string" );
187 fieldType = tr(
"integer" );
189 case QVariant::LongLong:
190 fieldType = tr(
"integer64" );
192 case QVariant::Double:
193 fieldType = tr(
"double" );
196 fieldType = tr(
"boolean" );
199 fieldType = tr(
"unknown type" );
203 mFieldNameList << f.name();
204 mFieldTypeList << fieldType;
217 mVectorLayer = layer;
222 const auto constMSiblingWidgets = mSiblingWidgets;
223 for (
const SiblingWidget &sw : constMSiblingWidgets )
225 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingCheckState )
228 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingCheckState, natural ) );
234 const auto constMSiblingWidgets = mSiblingWidgets;
235 for (
const SiblingWidget &sw : constMSiblingWidgets )
237 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingEnableState )
240 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingEnableState, natural ) );
246 const auto constMSiblingWidgets = mSiblingWidgets;
247 for (
const SiblingWidget &sw : constMSiblingWidgets )
249 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingVisibility )
252 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingVisibility, natural ) );
258 const auto constMSiblingWidgets = mSiblingWidgets;
259 for (
const SiblingWidget &sw : constMSiblingWidgets )
261 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingExpressionText )
264 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingExpressionText ) );
272 if ( ( event->modifiers() & ( Qt::ControlModifier ) )
273 || event->button() == Qt::RightButton )
275 setActivePrivate( !mProperty.
isActive() );
283 QToolButton::mousePressEvent( event );
297 mFieldName =
property.field();
302 mExpressionString =
property.expressionString();
310 mExpressionString.clear();
312 mProperty = property;
319 void QgsPropertyOverrideButton::aboutToShowMenu()
321 mDefineMenu->clear();
325 bool hasExp = !mExpressionString.isEmpty();
326 QString ddTitle = tr(
"Data defined override" );
328 QAction *ddTitleAct = mDefineMenu->addAction( ddTitle );
329 QFont titlefont = ddTitleAct->font();
330 titlefont.setItalic(
true );
331 ddTitleAct->setFont( titlefont );
332 ddTitleAct->setEnabled(
false );
334 bool addActiveAction =
false;
339 addActiveAction = !exp.hasParserError();
344 addActiveAction = mFieldNameList.contains( mFieldName );
347 if ( addActiveAction )
350 mDefineMenu->addAction( mActionActive );
351 mActionActive->setText( mProperty.
isActive() ? tr(
"Deactivate" ) : tr(
"Activate" ) );
352 mActionActive->setData( QVariant( !mProperty.
isActive() ) );
355 if ( !mFullDescription.isEmpty() )
357 mDefineMenu->addAction( mActionDescription );
360 mDefineMenu->addSeparator();
363 if ( mAuxiliaryStorageEnabled && mVectorLayer )
365 mDefineMenu->addAction( mActionCreateAuxiliaryField );
369 mActionCreateAuxiliaryField->setEnabled(
true );
370 mActionCreateAuxiliaryField->setChecked(
false );
374 if ( index >= 0 && alayer && mVectorLayer->
isAuxiliaryField( index, srcIndex ) )
376 mActionCreateAuxiliaryField->setEnabled(
false );
377 mActionCreateAuxiliaryField->setChecked(
true );
381 bool fieldActive =
false;
382 if ( !mDataTypesString.isEmpty() )
384 QAction *fieldTitleAct = mDefineMenu->addAction( tr(
"Attribute Field" ) );
385 fieldTitleAct->setFont( titlefont );
386 fieldTitleAct->setEnabled(
false );
388 mDefineMenu->addAction( mActionDataTypes );
390 mFieldsMenu->clear();
392 if ( !mFieldNameList.isEmpty() )
395 for (
int j = 0; j < mFieldNameList.count(); ++j )
397 QString fldname = mFieldNameList.at( j );
398 QAction *act = mFieldsMenu->addAction( fldname +
" (" + mFieldTypeList.at( j ) +
')' );
399 act->setData( QVariant( fldname ) );
400 if ( mFieldName == fldname )
402 act->setCheckable(
true );
410 QAction *act = mFieldsMenu->addAction( tr(
"No matching field types found" ) );
411 act->setEnabled(
false );
414 mDefineMenu->addSeparator();
417 mFieldsMenu->menuAction()->setCheckable(
true );
420 bool colorActive =
false;
421 mColorsMenu->clear();
426 QAction *colorTitleAct = mDefineMenu->addAction( tr(
"Project Color" ) );
427 colorTitleAct->setFont( titlefont );
428 colorTitleAct->setEnabled(
false );
430 QList<QgsProjectColorScheme *> projectSchemes;
432 if ( projectSchemes.length() > 0 )
436 for (
const auto &color : colors )
438 if ( color.second.isEmpty() )
442 QAction *act = mColorsMenu->addAction( color.second );
443 act->setIcon( icon );
446 act->setCheckable(
true );
447 act->setChecked(
true );
453 if ( mColorsMenu->actions().isEmpty() )
455 QAction *act = mColorsMenu->addAction( tr(
"No colors set" ) );
456 act->setEnabled(
false );
459 mDefineMenu->addAction( mActionColors );
460 mColorsMenu->menuAction()->setCheckable(
true );
461 mColorsMenu->menuAction()->setChecked( colorActive && !mProperty.
transformer() );
463 mDefineMenu->addSeparator();
466 QAction *exprTitleAct = mDefineMenu->addAction( tr(
"Expression" ) );
467 exprTitleAct->setFont( titlefont );
468 exprTitleAct->setEnabled(
false );
470 mVariablesMenu->clear();
471 bool variableActive =
false;
472 if ( mExpressionContextGenerator )
476 const auto constVariables = variables;
477 for (
const QString &variable : constVariables )
481 if ( variable.startsWith(
'_' ) )
484 QAction *act = mVariablesMenu->addAction( variable );
485 act->setData( QVariant( variable ) );
489 act->setCheckable(
true );
490 act->setChecked(
true );
491 variableActive =
true;
496 if ( mVariablesMenu->actions().isEmpty() )
498 QAction *act = mVariablesMenu->addAction( tr(
"No variables set" ) );
499 act->setEnabled(
false );
502 mDefineMenu->addAction( mActionVariables );
503 mVariablesMenu->menuAction()->setCheckable(
true );
504 mVariablesMenu->menuAction()->setChecked( variableActive && !mProperty.
transformer() );
508 QString expString = mExpressionString;
509 if ( expString.length() > 35 )
511 expString.truncate( 35 );
512 expString.append( QChar( 0x2026 ) );
515 expString.prepend( tr(
"Current: " ) );
517 if ( !mActionExpression )
519 mActionExpression =
new QAction( expString,
this );
520 mActionExpression->setCheckable(
true );
524 mActionExpression->setText( expString );
526 mDefineMenu->addAction( mActionExpression );
529 mDefineMenu->addAction( mActionExpDialog );
530 mDefineMenu->addAction( mActionCopyExpr );
531 mDefineMenu->addAction( mActionPasteExpr );
535 mDefineMenu->addAction( mActionExpDialog );
536 mDefineMenu->addAction( mActionPasteExpr );
539 if ( hasExp || !mFieldName.isEmpty() )
541 mDefineMenu->addSeparator();
542 mDefineMenu->addAction( mActionClearExpr );
547 mDefineMenu->addSeparator();
548 mActionAssistant->setCheckable( mProperty.
transformer() );
549 mActionAssistant->setChecked( mProperty.
transformer() );
550 mDefineMenu->addAction( mActionAssistant );
554 void QgsPropertyOverrideButton::menuActionTriggered( QAction *action )
556 if ( action == mActionActive )
558 setActivePrivate( mActionActive->data().toBool() );
562 else if ( action == mActionDescription )
564 showDescriptionDialog();
566 else if ( action == mActionExpDialog )
568 showExpressionDialog();
570 else if ( action == mActionExpression )
574 setActivePrivate(
true );
579 else if ( action == mActionCopyExpr )
581 QApplication::clipboard()->setText( mExpressionString );
583 else if ( action == mActionPasteExpr )
585 QString exprString = QApplication::clipboard()->text();
586 if ( !exprString.isEmpty() )
588 mExpressionString = exprString;
591 setActivePrivate(
true );
597 else if ( action == mActionClearExpr )
599 setActivePrivate(
false );
602 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 ( mExpressionString != QStringLiteral(
"project_color('%1')" ).arg( action->text() ) )
648 mExpressionString = QStringLiteral(
"project_color('%1')" ).arg( action->text() );
652 setActivePrivate(
true );
660 void QgsPropertyOverrideButton::showDescriptionDialog()
663 mv->setWindowTitle( tr(
"Data Definition Description" ) );
669 void QgsPropertyOverrideButton::showExpressionDialog()
678 d.setExpectedOutputFormat( mInputDescription );
679 if ( d.exec() == QDialog::Accepted )
681 mExpressionString = d.expressionText().trimmed();
684 setActivePrivate( !mExpressionString.isEmpty() );
692 void QgsPropertyOverrideButton::showAssistant()
712 mFieldName = this->mProperty.
field();
728 QDialog *dlg =
new QDialog(
this );
729 QString key = QStringLiteral(
"/UI/paneldialog/%1" ).arg( widget->
panelTitle() );
731 dlg->restoreGeometry( settings.
value( key ).toByteArray() );
733 dlg->setLayout(
new QVBoxLayout() );
734 dlg->layout()->addWidget( widget );
735 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
736 connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
737 connect( buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject );
738 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsPropertyOverrideButton::showHelp );
739 dlg->layout()->addWidget( buttonBox );
741 if ( dlg->exec() == QDialog::Accepted )
745 mFieldName = mProperty.
field();
752 settings.
setValue( key, dlg->saveGeometry() );
756 void QgsPropertyOverrideButton::updateGui()
758 bool hasExp = !mExpressionString.isEmpty();
759 bool hasField = !mFieldName.isEmpty();
762 QString deftip = tr(
"undefined" );
768 QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
769 QRegularExpressionMatch match = rx.match( mExpressionString );
770 if ( match.hasMatch() )
773 deftip = match.captured( 1 );
774 deftype = tr(
"project color" );
779 if ( exp.hasParserError() )
782 deftip = tr(
"Parse error: %1" ).arg( exp.parserErrorString() );
786 deftip = mExpressionString;
794 if ( !mFieldNameList.contains( mFieldName ) && !mProperty.
transformer() )
797 deftip = tr(
"'%1' field missing" ).arg( mFieldName );
808 mFullDescription = tr(
"<b><u>Data defined override</u></b><br>" );
810 mFullDescription += tr(
"<b>Active: </b>%1 <i>(ctrl|right-click toggles)</i><br>" ).arg( mProperty.
isActive() ? tr(
"yes" ) : tr(
"no" ) );
812 if ( !mUsageInfo.isEmpty() )
814 mFullDescription += tr(
"<b>Usage:</b><br>%1<br>" ).arg( mUsageInfo );
817 if ( !mInputDescription.isEmpty() )
819 mFullDescription += tr(
"<b>Expected input:</b><br>%1<br>" ).arg( mInputDescription );
822 if ( !mDataTypesString.isEmpty() )
824 mFullDescription += tr(
"<b>Valid input types:</b><br>%1<br>" ).arg( mDataTypesString );
827 if ( deftype.isEmpty() && deftip != tr(
"undefined" ) )
833 if ( deftip.length() > 75 )
835 deftip.truncate( 75 );
836 deftip.append( QChar( 0x2026 ) );
839 mFullDescription += tr(
"<b>Current definition (%1):</b><br>%2" ).arg( deftype, deftip );
841 setToolTip( mFullDescription );
845 void QgsPropertyOverrideButton::setActivePrivate(
bool active )
854 void QgsPropertyOverrideButton::updateSiblingWidgets(
bool state )
856 const auto constMSiblingWidgets = mSiblingWidgets;
857 for (
const SiblingWidget &sw : constMSiblingWidgets )
859 switch ( sw.mSiblingType )
862 case SiblingCheckState:
867 QAbstractButton *btn = qobject_cast< QAbstractButton * >( sw.mWidgetPointer.data() );
868 if ( btn && btn->isCheckable() )
870 btn->setChecked( sw.mNatural ? state : !state );
874 QGroupBox *grpbx = qobject_cast< QGroupBox * >( sw.mWidgetPointer.data() );
875 if ( grpbx && grpbx->isCheckable() )
877 grpbx->setChecked( sw.mNatural ? state : !state );
884 case SiblingEnableState:
886 QLineEdit *le = qobject_cast< QLineEdit * >( sw.mWidgetPointer.data() );
888 le->setReadOnly( sw.mNatural ? !state : state );
890 sw.mWidgetPointer.data()->setEnabled( sw.mNatural ? state : !state );
894 case SiblingVisibility:
896 sw.mWidgetPointer.data()->setVisible( sw.mNatural ? state : !state );
900 case SiblingExpressionText:
902 QLineEdit *le = qobject_cast<QLineEdit *>( sw.mWidgetPointer.data() );
909 QTextEdit *te = qobject_cast<QTextEdit *>( sw.mWidgetPointer.data() );
918 case SiblingLinkedWidget:
920 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( sw.mWidgetPointer.data() ) )
924 QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
925 QRegularExpressionMatch match = rx.match( mExpressionString );
926 if ( match.hasMatch() )
928 cb->linkToProjectColor( match.captured( 1 ) );
933 cb->linkToProjectColor( QString() );
956 mExpressionContextGenerator = generator;
961 for (
const SiblingWidget &sw : qgis::as_const( mSiblingWidgets ) )
963 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingLinkedWidget )
966 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingLinkedWidget ) );
968 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( widget ) )
980 void QgsPropertyOverrideButton::showHelp()
982 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)
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.
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.