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();
734 QDialog *dlg =
new QDialog(
this );
735 QString key = QStringLiteral(
"/UI/paneldialog/%1" ).arg( widget->
panelTitle() );
736 QgsSettings settings;
737 dlg->restoreGeometry( settings.value( key ).toByteArray() );
739 dlg->setLayout(
new QVBoxLayout() );
740 dlg->layout()->addWidget( widget );
741 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok );
742 connect( buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
743 connect( buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject );
744 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsPropertyOverrideButton::showHelp );
745 dlg->layout()->addWidget( buttonBox );
747 if ( dlg->exec() == QDialog::Accepted )
751 mFieldName = mProperty.
field();
758 settings.setValue( key, dlg->saveGeometry() );
762 void QgsPropertyOverrideButton::updateGui()
764 bool hasExp = !mExpressionString.isEmpty();
765 bool hasField = !mFieldName.isEmpty();
768 QString deftip = tr(
"undefined" );
774 QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
775 QRegularExpressionMatch match = rx.match( mExpressionString );
776 if ( match.hasMatch() )
779 deftip = match.captured( 1 );
780 deftype = tr(
"project color" );
785 if ( exp.hasParserError() )
788 deftip = tr(
"Parse error: %1" ).arg( exp.parserErrorString() );
792 deftip = mExpressionString;
800 if ( !mFieldNameList.contains( mFieldName ) && !mProperty.
transformer() )
803 deftip = tr(
"'%1' field missing" ).arg( mFieldName );
814 mFullDescription = tr(
"<b><u>Data defined override</u></b><br>" );
816 mFullDescription += tr(
"<b>Active: </b>%1 <i>(ctrl|right-click toggles)</i><br>" ).arg( mProperty.
isActive() ? tr(
"yes" ) : tr(
"no" ) );
818 if ( !mUsageInfo.isEmpty() )
820 mFullDescription += tr(
"<b>Usage:</b><br>%1<br>" ).arg( mUsageInfo );
823 if ( !mInputDescription.isEmpty() )
825 mFullDescription += tr(
"<b>Expected input:</b><br>%1<br>" ).arg( mInputDescription );
828 if ( !mDataTypesString.isEmpty() )
830 mFullDescription += tr(
"<b>Valid input types:</b><br>%1<br>" ).arg( mDataTypesString );
833 if ( deftype.isEmpty() && deftip != tr(
"undefined" ) )
839 if ( deftip.length() > 75 )
841 deftip.truncate( 75 );
842 deftip.append( QChar( 0x2026 ) );
845 mFullDescription += tr(
"<b>Current definition (%1):</b><br>%2" ).arg( deftype, deftip );
847 setToolTip( mFullDescription );
851 void QgsPropertyOverrideButton::setActivePrivate(
bool active )
860 void QgsPropertyOverrideButton::updateSiblingWidgets(
bool state )
862 const auto constMSiblingWidgets = mSiblingWidgets;
863 for (
const SiblingWidget &sw : constMSiblingWidgets )
865 switch ( sw.mSiblingType )
868 case SiblingCheckState:
873 QAbstractButton *btn = qobject_cast< QAbstractButton * >( sw.mWidgetPointer.data() );
874 if ( btn && btn->isCheckable() )
876 btn->setChecked( sw.mNatural ? state : !state );
880 QGroupBox *grpbx = qobject_cast< QGroupBox * >( sw.mWidgetPointer.data() );
881 if ( grpbx && grpbx->isCheckable() )
883 grpbx->setChecked( sw.mNatural ? state : !state );
890 case SiblingEnableState:
892 QLineEdit *le = qobject_cast< QLineEdit * >( sw.mWidgetPointer.data() );
894 le->setReadOnly( sw.mNatural ? !state : state );
896 sw.mWidgetPointer.data()->setEnabled( sw.mNatural ? state : !state );
900 case SiblingVisibility:
902 sw.mWidgetPointer.data()->setVisible( sw.mNatural ? state : !state );
906 case SiblingExpressionText:
908 QLineEdit *le = qobject_cast<QLineEdit *>( sw.mWidgetPointer.data() );
915 QTextEdit *te = qobject_cast<QTextEdit *>( sw.mWidgetPointer.data() );
924 case SiblingLinkedWidget:
926 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( sw.mWidgetPointer.data() ) )
930 QRegularExpression rx( QStringLiteral(
"^project_color\\('(.*)'\\)$" ) );
931 QRegularExpressionMatch match = rx.match( mExpressionString );
932 if ( match.hasMatch() )
934 cb->linkToProjectColor( match.captured( 1 ) );
939 cb->linkToProjectColor( QString() );
962 mExpressionContextGenerator = generator;
967 for (
const SiblingWidget &sw : std::as_const( mSiblingWidgets ) )
969 if ( widget == sw.mWidgetPointer.data() && sw.mSiblingType == SiblingLinkedWidget )
972 mSiblingWidgets.append( SiblingWidget( QPointer<QWidget>( widget ), SiblingLinkedWidget ) );
974 if (
QgsColorButton *cb = qobject_cast< QgsColorButton * >( widget ) )
986 void QgsPropertyOverrideButton::showHelp()
988 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.
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.