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;
168 switch ( mDataTypes )
175 fieldMatch = f.isNumeric() || f.type() == QVariant::String;
179 fieldMatch = f.type() == QVariant::String;
185 case QVariant::String:
186 fieldType = tr(
"string" );
189 fieldType = tr(
"integer" );
191 case QVariant::LongLong:
192 fieldType = tr(
"integer64" );
194 case QVariant::Double:
195 fieldType = tr(
"double" );
198 fieldType = tr(
"boolean" );
201 fieldType = tr(
"unknown type" );
205 mFieldNameList << f.name();
206 mFieldDisplayNameList << f.displayNameWithAlias();
221 mVectorLayer = layer;
226 const auto constMSiblingWidgets = mSiblingWidgets;
227 for (
const SiblingWidget &sw : constMSiblingWidgets )
229 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingCheckState )
232 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingCheckState, natural ) );
238 const auto constMSiblingWidgets = mSiblingWidgets;
239 for (
const SiblingWidget &sw : constMSiblingWidgets )
241 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingEnableState )
244 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingEnableState, natural ) );
250 const auto constMSiblingWidgets = mSiblingWidgets;
251 for (
const SiblingWidget &sw : constMSiblingWidgets )
253 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingVisibility )
256 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingVisibility, natural ) );
262 const auto constMSiblingWidgets = mSiblingWidgets;
263 for (
const SiblingWidget &sw : constMSiblingWidgets )
265 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingExpressionText )
268 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingExpressionText ) );
276 if ( ( event->modifiers() & ( Qt::ControlModifier ) )
277 || event->button() == Qt::RightButton )
279 setActivePrivate( !mProperty.
isActive() );
287 QToolButton::mousePressEvent( event );
301 mFieldName =
property.field();
306 mExpressionString =
property.expressionString();
314 mExpressionString.clear();
316 mProperty = property;
323 void QgsPropertyOverrideButton::aboutToShowMenu()
325 mDefineMenu->clear();
329 bool hasExp = !mExpressionString.isEmpty();
330 QString ddTitle = tr(
"Data defined override" );
332 QAction *ddTitleAct = mDefineMenu->addAction( ddTitle );
333 QFont titlefont = ddTitleAct->font();
334 titlefont.setItalic(
true );
335 ddTitleAct->setFont( titlefont );
336 ddTitleAct->setEnabled(
false );
338 bool addActiveAction =
false;
343 addActiveAction = !exp.hasParserError();
348 addActiveAction = mFieldNameList.contains( mFieldName );
351 if ( addActiveAction )
354 mDefineMenu->addAction( mActionActive );
355 mActionActive->setText( mProperty.
isActive() ? tr(
"Deactivate" ) : tr(
"Activate" ) );
356 mActionActive->setData( QVariant( !mProperty.
isActive() ) );
359 if ( !mFullDescription.isEmpty() )
361 mDefineMenu->addAction( mActionDescription );
364 mDefineMenu->addSeparator();
367 if ( mAuxiliaryStorageEnabled && mVectorLayer )
369 mDefineMenu->addAction( mActionCreateAuxiliaryField );
373 mActionCreateAuxiliaryField->setEnabled(
true );
374 mActionCreateAuxiliaryField->setChecked(
false );
378 if ( index >= 0 && alayer && mVectorLayer->
isAuxiliaryField( index, srcIndex ) )
380 mActionCreateAuxiliaryField->setEnabled(
false );
381 mActionCreateAuxiliaryField->setChecked(
true );
385 bool fieldActive =
false;
386 if ( !mDataTypesString.isEmpty() )
388 QAction *fieldTitleAct = mDefineMenu->addAction( tr(
"Attribute Field" ) );
389 fieldTitleAct->setFont( titlefont );
390 fieldTitleAct->setEnabled(
false );
392 mDefineMenu->addAction( mActionDataTypes );
394 mFieldsMenu->clear();
396 if ( !mFieldNameList.isEmpty() )
399 for (
int j = 0; j < mFieldNameList.count(); ++j )
401 QString fldname = mFieldNameList.at( j );
402 QAction *act = mFieldsMenu->addAction( mFieldDisplayNameList.at( j ) );
403 act->setIcon( mFieldIcons.at( j ) );
404 act->setData( QVariant( fldname ) );
405 if ( mFieldName == fldname )
407 act->setCheckable(
true );
415 QAction *act = mFieldsMenu->addAction( tr(
"No matching field types found" ) );
416 act->setEnabled(
false );
419 mDefineMenu->addSeparator();
422 mFieldsMenu->menuAction()->setCheckable(
true );
425 bool colorActive =
false;
426 mColorsMenu->clear();
431 QAction *colorTitleAct = mDefineMenu->addAction( tr(
"Project Color" ) );
432 colorTitleAct->setFont( titlefont );
433 colorTitleAct->setEnabled(
false );
435 QList<QgsProjectColorScheme *> projectSchemes;
437 if ( projectSchemes.length() > 0 )
441 for (
const auto &color : colors )
443 if ( color.second.isEmpty() )
447 QAction *act = mColorsMenu->addAction( color.second );
448 act->setIcon( icon );
451 act->setCheckable(
true );
452 act->setChecked(
true );
458 if ( mColorsMenu->actions().isEmpty() )
460 QAction *act = mColorsMenu->addAction( tr(
"No colors set" ) );
461 act->setEnabled(
false );
464 mDefineMenu->addAction( mActionColors );
465 mColorsMenu->menuAction()->setCheckable(
true );
466 mColorsMenu->menuAction()->setChecked( colorActive && !mProperty.
transformer() );
468 mDefineMenu->addSeparator();
471 QAction *exprTitleAct = mDefineMenu->addAction( tr(
"Expression" ) );
472 exprTitleAct->setFont( titlefont );
473 exprTitleAct->setEnabled(
false );
475 mVariablesMenu->clear();
476 bool variableActive =
false;
477 if ( mExpressionContextGenerator )
481 const auto constVariables = variables;
482 for (
const QString &variable : constVariables )
486 if ( variable.startsWith(
'_' ) )
489 QAction *act = mVariablesMenu->addAction( variable );
490 act->setData( QVariant( variable ) );
494 act->setCheckable(
true );
495 act->setChecked(
true );
496 variableActive =
true;
501 if ( mVariablesMenu->actions().isEmpty() )
503 QAction *act = mVariablesMenu->addAction( tr(
"No variables set" ) );
504 act->setEnabled(
false );
507 mDefineMenu->addAction( mActionVariables );
508 mVariablesMenu->menuAction()->setCheckable(
true );
509 mVariablesMenu->menuAction()->setChecked( variableActive && !mProperty.
transformer() );
513 QString expString = mExpressionString;
514 if ( expString.length() > 35 )
516 expString.truncate( 35 );
517 expString.append( QChar( 0x2026 ) );
520 expString.prepend( tr(
"Current: " ) );
522 if ( !mActionExpression )
524 mActionExpression =
new QAction( expString,
this );
525 mActionExpression->setCheckable(
true );
529 mActionExpression->setText( expString );
531 mDefineMenu->addAction( mActionExpression );
534 mDefineMenu->addAction( mActionExpDialog );
535 mDefineMenu->addAction( mActionCopyExpr );
536 mDefineMenu->addAction( mActionPasteExpr );
540 mDefineMenu->addAction( mActionExpDialog );
541 mDefineMenu->addAction( mActionPasteExpr );
544 if ( hasExp || !mFieldName.isEmpty() )
546 mDefineMenu->addSeparator();
547 mDefineMenu->addAction( mActionClearExpr );
552 mDefineMenu->addSeparator();
553 mActionAssistant->setCheckable( mProperty.
transformer() );
554 mActionAssistant->setChecked( mProperty.
transformer() );
555 mDefineMenu->addAction( mActionAssistant );
559 void QgsPropertyOverrideButton::menuActionTriggered( QAction *action )
561 if ( action == mActionActive )
563 setActivePrivate( mActionActive->data().toBool() );
567 else if ( action == mActionDescription )
569 showDescriptionDialog();
571 else if ( action == mActionExpDialog )
573 showExpressionDialog();
575 else if ( action == mActionExpression )
579 setActivePrivate(
true );
584 else if ( action == mActionCopyExpr )
586 QApplication::clipboard()->setText( mExpressionString );
588 else if ( action == mActionPasteExpr )
590 QString exprString = QApplication::clipboard()->text();
591 if ( !exprString.isEmpty() )
593 mExpressionString = exprString;
596 setActivePrivate(
true );
602 else if ( action == mActionClearExpr )
604 setActivePrivate(
false );
607 mExpressionString.clear();
613 else if ( action == mActionAssistant )
617 else if ( action == mActionCreateAuxiliaryField )
621 else if ( mFieldsMenu->actions().contains( action ) )
623 if ( action->isEnabled() )
625 if ( mFieldName != action->text() )
627 mFieldName = action->data().toString();
631 setActivePrivate(
true );
637 else if ( mVariablesMenu->actions().contains( action ) )
639 if ( mExpressionString != action->text().prepend(
"@" ) )
641 mExpressionString = action->data().toString().prepend(
"@" );
645 setActivePrivate(
true );
650 else if ( mColorsMenu->actions().contains( action ) )
652 if ( mExpressionString != QStringLiteral(
"project_color('%1')" ).arg( action->text() ) )
654 mExpressionString = QStringLiteral(
"project_color('%1')" ).arg( action->text() );
658 setActivePrivate(
true );
666 void QgsPropertyOverrideButton::showDescriptionDialog()
669 mv->setWindowTitle( tr(
"Data Definition Description" ) );
675 void QgsPropertyOverrideButton::showExpressionDialog()
684 d.setExpectedOutputFormat( mInputDescription );
685 if ( d.exec() == QDialog::Accepted )
687 mExpressionString = d.expressionText().trimmed();
690 setActivePrivate( !mExpressionString.isEmpty() );
698 void QgsPropertyOverrideButton::showAssistant()
718 mFieldName = this->mProperty.
field();
735 QDialog *dlg =
new QDialog(
this );
736 QString key = QStringLiteral(
"/UI/paneldialog/%1" ).arg( widget->
panelTitle() );
738 dlg->restoreGeometry( settings.
value( key ).toByteArray() );
740 dlg->setLayout(
new QVBoxLayout() );
741 dlg->layout()->addWidget( widget );
742 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
743 connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
744 connect( buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject );
745 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsPropertyOverrideButton::showHelp );
746 dlg->layout()->addWidget( buttonBox );
748 if ( dlg->exec() == QDialog::Accepted )
752 mFieldName = mProperty.
field();
759 settings.
setValue( key, dlg->saveGeometry() );
763 void QgsPropertyOverrideButton::updateGui()
765 bool hasExp = !mExpressionString.isEmpty();
766 bool hasField = !mFieldName.isEmpty();
769 QString deftip = tr(
"undefined" );
775 QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
776 QRegularExpressionMatch match = rx.match( mExpressionString );
777 if ( match.hasMatch() )
780 deftip = match.captured( 1 );
781 deftype = tr(
"project color" );
786 if ( exp.hasParserError() )
789 deftip = tr(
"Parse error: %1" ).arg( exp.parserErrorString() );
793 deftip = mExpressionString;
801 if ( !mFieldNameList.contains( mFieldName ) && !mProperty.
transformer() )
804 deftip = tr(
"'%1' field missing" ).arg( mFieldName );
815 mFullDescription = tr(
"<b><u>Data defined override</u></b><br>" );
817 mFullDescription += tr(
"<b>Active: </b>%1 <i>(ctrl|right-click toggles)</i><br>" ).arg( mProperty.
isActive() ? tr(
"yes" ) : tr(
"no" ) );
819 if ( !mUsageInfo.isEmpty() )
821 mFullDescription += tr(
"<b>Usage:</b><br>%1<br>" ).arg( mUsageInfo );
824 if ( !mInputDescription.isEmpty() )
826 mFullDescription += tr(
"<b>Expected input:</b><br>%1<br>" ).arg( mInputDescription );
829 if ( !mDataTypesString.isEmpty() )
831 mFullDescription += tr(
"<b>Valid input types:</b><br>%1<br>" ).arg( mDataTypesString );
834 if ( deftype.isEmpty() && deftip != tr(
"undefined" ) )
840 if ( deftip.length() > 75 )
842 deftip.truncate( 75 );
843 deftip.append( QChar( 0x2026 ) );
846 mFullDescription += tr(
"<b>Current definition (%1):</b><br>%2" ).arg( deftype, deftip );
848 setToolTip( mFullDescription );
852 void QgsPropertyOverrideButton::setActivePrivate(
bool active )
861 void QgsPropertyOverrideButton::updateSiblingWidgets(
bool state )
863 const auto constMSiblingWidgets = mSiblingWidgets;
864 for (
const SiblingWidget &sw : constMSiblingWidgets )
866 switch ( sw.mSiblingType )
869 case SiblingCheckState:
874 QAbstractButton *btn = qobject_cast< QAbstractButton * >( sw.mWidgetPointer.data() );
875 if ( btn && btn->isCheckable() )
877 btn->setChecked( sw.mNatural ? state : !state );
881 QGroupBox *grpbx = qobject_cast< QGroupBox * >( sw.mWidgetPointer.data() );
882 if ( grpbx && grpbx->isCheckable() )
884 grpbx->setChecked( sw.mNatural ? state : !state );
891 case SiblingEnableState:
893 QLineEdit *le = qobject_cast< QLineEdit * >( sw.mWidgetPointer.data() );
895 le->setReadOnly( sw.mNatural ? !state : state );
897 sw.mWidgetPointer.data()->setEnabled( sw.mNatural ? state : !state );
901 case SiblingVisibility:
903 sw.mWidgetPointer.data()->setVisible( sw.mNatural ? state : !state );
907 case SiblingExpressionText:
909 QLineEdit *le = qobject_cast<QLineEdit *>( sw.mWidgetPointer.data() );
916 QTextEdit *te = qobject_cast<QTextEdit *>( sw.mWidgetPointer.data() );
925 case SiblingLinkedWidget:
927 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( sw.mWidgetPointer.data() ) )
931 QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
932 QRegularExpressionMatch match = rx.match( mExpressionString );
933 if ( match.hasMatch() )
935 cb->linkToProjectColor( match.captured( 1 ) );
940 cb->linkToProjectColor( QString() );
963 mExpressionContextGenerator = generator;
968 for (
const SiblingWidget &sw : std::as_const( mSiblingWidgets ) )
970 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingLinkedWidget )
973 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingLinkedWidget ) );
975 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( widget ) )
987 void QgsPropertyOverrideButton::showHelp()
989 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.