mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-05 00:09:32 -04:00
Add API for custom preview generators in QgsExpressionBuilderWidget
In this mode, the widget will call a callback function to generate a new QgsExpressionContext as the previewed object changes. This can be used to provide custom preview values for different objects (i.e. for objects which aren't vector layer features), such as raster bands or other custom objects.
This commit is contained in:
parent
aee2805234
commit
d9a49f58fd
@ -159,6 +159,45 @@ preview result and to populate the list of available functions and variables.
|
||||
Returns if the expression is valid
|
||||
%End
|
||||
|
||||
|
||||
void setCustomPreviewGenerator( const QString &label, const QList< QPair< QString, QVariant > > &choices, SIP_PYCALLABLE );
|
||||
%Docstring
|
||||
Sets the widget to run using a custom preview generator.
|
||||
|
||||
In this mode, the widget will call a callback function to generate a new :py:class:`QgsExpressionContext`
|
||||
as the previewed object changes. This can be used to provide custom preview values for different
|
||||
objects (i.e. for objects which aren't vector layer features).
|
||||
|
||||
:param label: The label to display for the combo box presenting choices of objects. This should be a representative name, eg "Band" if the widget is showing choices of raster layer bands
|
||||
:param choices: A list of choices to present to the user. Each choice is a pair of a human-readable label and a QVariant representing the object to preview.
|
||||
:param previewContextGenerator: A function which takes a QVariant representing the object to preview, and returns a :py:class:`QgsExpressionContext` to use for previewing the object.
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
%MethodCode
|
||||
Py_XINCREF( a2 );
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
sipCpp->setCustomPreviewGenerator( *a0, *a1, [a2]( const QVariant &value )->QgsExpressionContext
|
||||
{
|
||||
QgsExpressionContext res;
|
||||
SIP_BLOCK_THREADS
|
||||
PyObject *s = sipCallMethod( NULL, a2, "D", &value, sipType_QVariant, NULL );
|
||||
int state;
|
||||
int sipIsError = 0;
|
||||
QgsExpressionContext *t1 = reinterpret_cast<QgsExpressionContext *>( sipConvertToType( s, sipType_QgsExpressionContext, 0, SIP_NOT_NONE, &state, &sipIsError ) );
|
||||
if ( sipIsError == 0 )
|
||||
{
|
||||
res = QgsExpressionContext( *t1 );
|
||||
}
|
||||
sipReleaseType( t1, sipType_QgsExpressionContext, state );
|
||||
SIP_UNBLOCK_THREADS
|
||||
return res;
|
||||
} );
|
||||
|
||||
Py_END_ALLOW_THREADS
|
||||
%End
|
||||
|
||||
|
||||
void saveToRecent( const QString &collection = "generic" ) /Deprecated/;
|
||||
%Docstring
|
||||
Adds the current expression to the given ``collection``.
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsExpressionPreviewWidget : QWidget
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
@ -34,6 +35,44 @@ Constructor
|
||||
Sets the layer used in the preview
|
||||
%End
|
||||
|
||||
|
||||
void setCustomPreviewGenerator( const QString &label, const QList< QPair< QString, QVariant > > &choices, SIP_PYCALLABLE );
|
||||
%Docstring
|
||||
Sets the widget to run using a custom preview generator.
|
||||
|
||||
In this mode, the widget will call a callback function to generate a new :py:class:`QgsExpressionContext`
|
||||
as the previewed object changes. This can be used to provide custom preview values for different
|
||||
objects (i.e. for objects which aren't vector layer features).
|
||||
|
||||
:param label: The label to display for the combo box presenting choices of objects. This should be a representative name, eg "Band" if the widget is showing choices of raster layer bands
|
||||
:param choices: A list of choices to present to the user. Each choice is a pair of a human-readable label and a QVariant representing the object to preview.
|
||||
:param previewContextGenerator: A function which takes a QVariant representing the object to preview, and returns a :py:class:`QgsExpressionContext` to use for previewing the object.
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
%MethodCode
|
||||
Py_XINCREF( a2 );
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
sipCpp->setCustomPreviewGenerator( *a0, *a1, [a2]( const QVariant &value )->QgsExpressionContext
|
||||
{
|
||||
QgsExpressionContext res;
|
||||
SIP_BLOCK_THREADS
|
||||
PyObject *s = sipCallMethod( NULL, a2, "D", &value, sipType_QVariant, NULL );
|
||||
int state;
|
||||
int sipIsError = 0;
|
||||
QgsExpressionContext *t1 = reinterpret_cast<QgsExpressionContext *>( sipConvertToType( s, sipType_QgsExpressionContext, 0, SIP_NOT_NONE, &state, &sipIsError ) );
|
||||
if ( sipIsError == 0 )
|
||||
{
|
||||
res = QgsExpressionContext( *t1 );
|
||||
}
|
||||
sipReleaseType( t1, sipType_QgsExpressionContext, state );
|
||||
SIP_UNBLOCK_THREADS
|
||||
return res;
|
||||
} );
|
||||
|
||||
Py_END_ALLOW_THREADS
|
||||
%End
|
||||
|
||||
void setExpressionText( const QString &expression );
|
||||
%Docstring
|
||||
Sets the expression
|
||||
@ -80,6 +119,13 @@ Returns the root node of the expression
|
||||
QList<QgsExpression::ParserError> parserErrors() const;
|
||||
%Docstring
|
||||
Returns the expression parser errors
|
||||
%End
|
||||
|
||||
QString currentPreviewText() const;
|
||||
%Docstring
|
||||
Returns the current expression result preview text.
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
|
||||
signals:
|
||||
|
@ -142,6 +142,44 @@ an expression context for the widget.
|
||||
create an expression context when required.
|
||||
%End
|
||||
|
||||
|
||||
void setCustomPreviewGenerator( const QString &label, const QList< QPair< QString, QVariant > > &choices, SIP_PYCALLABLE );
|
||||
%Docstring
|
||||
Sets the widget to run using a custom preview generator.
|
||||
|
||||
In this mode, the widget will call a callback function to generate a new :py:class:`QgsExpressionContext`
|
||||
as the previewed object changes. This can be used to provide custom preview values for different
|
||||
objects (i.e. for objects which aren't vector layer features).
|
||||
|
||||
:param label: The label to display for the combo box presenting choices of objects. This should be a representative name, eg "Band" if the widget is showing choices of raster layer bands
|
||||
:param choices: A list of choices to present to the user. Each choice is a pair of a human-readable label and a QVariant representing the object to preview.
|
||||
:param previewContextGenerator: A function which takes a QVariant representing the object to preview, and returns a :py:class:`QgsExpressionContext` to use for previewing the object.
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
%MethodCode
|
||||
Py_XINCREF( a2 );
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
sipCpp->setCustomPreviewGenerator( *a0, *a1, [a2]( const QVariant &value )->QgsExpressionContext
|
||||
{
|
||||
QgsExpressionContext res;
|
||||
SIP_BLOCK_THREADS
|
||||
PyObject *s = sipCallMethod( NULL, a2, "D", &value, sipType_QVariant, NULL );
|
||||
int state;
|
||||
int sipIsError = 0;
|
||||
QgsExpressionContext *t1 = reinterpret_cast<QgsExpressionContext *>( sipConvertToType( s, sipType_QgsExpressionContext, 0, SIP_NOT_NONE, &state, &sipIsError ) );
|
||||
if ( sipIsError == 0 )
|
||||
{
|
||||
res = QgsExpressionContext( *t1 );
|
||||
}
|
||||
sipReleaseType( t1, sipType_QgsExpressionContext, state );
|
||||
SIP_UNBLOCK_THREADS
|
||||
return res;
|
||||
} );
|
||||
|
||||
Py_END_ALLOW_THREADS
|
||||
%End
|
||||
|
||||
bool allowEvalErrors() const;
|
||||
%Docstring
|
||||
Allow accepting expressions with evaluation errors. This can be useful when we are not able to
|
||||
|
@ -159,6 +159,45 @@ preview result and to populate the list of available functions and variables.
|
||||
Returns if the expression is valid
|
||||
%End
|
||||
|
||||
|
||||
void setCustomPreviewGenerator( const QString &label, const QList< QPair< QString, QVariant > > &choices, SIP_PYCALLABLE );
|
||||
%Docstring
|
||||
Sets the widget to run using a custom preview generator.
|
||||
|
||||
In this mode, the widget will call a callback function to generate a new :py:class:`QgsExpressionContext`
|
||||
as the previewed object changes. This can be used to provide custom preview values for different
|
||||
objects (i.e. for objects which aren't vector layer features).
|
||||
|
||||
:param label: The label to display for the combo box presenting choices of objects. This should be a representative name, eg "Band" if the widget is showing choices of raster layer bands
|
||||
:param choices: A list of choices to present to the user. Each choice is a pair of a human-readable label and a QVariant representing the object to preview.
|
||||
:param previewContextGenerator: A function which takes a QVariant representing the object to preview, and returns a :py:class:`QgsExpressionContext` to use for previewing the object.
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
%MethodCode
|
||||
Py_XINCREF( a2 );
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
sipCpp->setCustomPreviewGenerator( *a0, *a1, [a2]( const QVariant &value )->QgsExpressionContext
|
||||
{
|
||||
QgsExpressionContext res;
|
||||
SIP_BLOCK_THREADS
|
||||
PyObject *s = sipCallMethod( NULL, a2, "D", &value, sipType_QVariant, NULL );
|
||||
int state;
|
||||
int sipIsError = 0;
|
||||
QgsExpressionContext *t1 = reinterpret_cast<QgsExpressionContext *>( sipConvertToType( s, sipType_QgsExpressionContext, 0, SIP_NOT_NONE, &state, &sipIsError ) );
|
||||
if ( sipIsError == 0 )
|
||||
{
|
||||
res = QgsExpressionContext( *t1 );
|
||||
}
|
||||
sipReleaseType( t1, sipType_QgsExpressionContext, state );
|
||||
SIP_UNBLOCK_THREADS
|
||||
return res;
|
||||
} );
|
||||
|
||||
Py_END_ALLOW_THREADS
|
||||
%End
|
||||
|
||||
|
||||
void saveToRecent( const QString &collection = "generic" ) /Deprecated/;
|
||||
%Docstring
|
||||
Adds the current expression to the given ``collection``.
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsExpressionPreviewWidget : QWidget
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
@ -34,6 +35,44 @@ Constructor
|
||||
Sets the layer used in the preview
|
||||
%End
|
||||
|
||||
|
||||
void setCustomPreviewGenerator( const QString &label, const QList< QPair< QString, QVariant > > &choices, SIP_PYCALLABLE );
|
||||
%Docstring
|
||||
Sets the widget to run using a custom preview generator.
|
||||
|
||||
In this mode, the widget will call a callback function to generate a new :py:class:`QgsExpressionContext`
|
||||
as the previewed object changes. This can be used to provide custom preview values for different
|
||||
objects (i.e. for objects which aren't vector layer features).
|
||||
|
||||
:param label: The label to display for the combo box presenting choices of objects. This should be a representative name, eg "Band" if the widget is showing choices of raster layer bands
|
||||
:param choices: A list of choices to present to the user. Each choice is a pair of a human-readable label and a QVariant representing the object to preview.
|
||||
:param previewContextGenerator: A function which takes a QVariant representing the object to preview, and returns a :py:class:`QgsExpressionContext` to use for previewing the object.
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
%MethodCode
|
||||
Py_XINCREF( a2 );
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
sipCpp->setCustomPreviewGenerator( *a0, *a1, [a2]( const QVariant &value )->QgsExpressionContext
|
||||
{
|
||||
QgsExpressionContext res;
|
||||
SIP_BLOCK_THREADS
|
||||
PyObject *s = sipCallMethod( NULL, a2, "D", &value, sipType_QVariant, NULL );
|
||||
int state;
|
||||
int sipIsError = 0;
|
||||
QgsExpressionContext *t1 = reinterpret_cast<QgsExpressionContext *>( sipConvertToType( s, sipType_QgsExpressionContext, 0, SIP_NOT_NONE, &state, &sipIsError ) );
|
||||
if ( sipIsError == 0 )
|
||||
{
|
||||
res = QgsExpressionContext( *t1 );
|
||||
}
|
||||
sipReleaseType( t1, sipType_QgsExpressionContext, state );
|
||||
SIP_UNBLOCK_THREADS
|
||||
return res;
|
||||
} );
|
||||
|
||||
Py_END_ALLOW_THREADS
|
||||
%End
|
||||
|
||||
void setExpressionText( const QString &expression );
|
||||
%Docstring
|
||||
Sets the expression
|
||||
@ -80,6 +119,13 @@ Returns the root node of the expression
|
||||
QList<QgsExpression::ParserError> parserErrors() const;
|
||||
%Docstring
|
||||
Returns the expression parser errors
|
||||
%End
|
||||
|
||||
QString currentPreviewText() const;
|
||||
%Docstring
|
||||
Returns the current expression result preview text.
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
|
||||
signals:
|
||||
|
@ -142,6 +142,44 @@ an expression context for the widget.
|
||||
create an expression context when required.
|
||||
%End
|
||||
|
||||
|
||||
void setCustomPreviewGenerator( const QString &label, const QList< QPair< QString, QVariant > > &choices, SIP_PYCALLABLE );
|
||||
%Docstring
|
||||
Sets the widget to run using a custom preview generator.
|
||||
|
||||
In this mode, the widget will call a callback function to generate a new :py:class:`QgsExpressionContext`
|
||||
as the previewed object changes. This can be used to provide custom preview values for different
|
||||
objects (i.e. for objects which aren't vector layer features).
|
||||
|
||||
:param label: The label to display for the combo box presenting choices of objects. This should be a representative name, eg "Band" if the widget is showing choices of raster layer bands
|
||||
:param choices: A list of choices to present to the user. Each choice is a pair of a human-readable label and a QVariant representing the object to preview.
|
||||
:param previewContextGenerator: A function which takes a QVariant representing the object to preview, and returns a :py:class:`QgsExpressionContext` to use for previewing the object.
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
%MethodCode
|
||||
Py_XINCREF( a2 );
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
sipCpp->setCustomPreviewGenerator( *a0, *a1, [a2]( const QVariant &value )->QgsExpressionContext
|
||||
{
|
||||
QgsExpressionContext res;
|
||||
SIP_BLOCK_THREADS
|
||||
PyObject *s = sipCallMethod( NULL, a2, "D", &value, sipType_QVariant, NULL );
|
||||
int state;
|
||||
int sipIsError = 0;
|
||||
QgsExpressionContext *t1 = reinterpret_cast<QgsExpressionContext *>( sipConvertToType( s, sipType_QgsExpressionContext, 0, SIP_NOT_NONE, &state, &sipIsError ) );
|
||||
if ( sipIsError == 0 )
|
||||
{
|
||||
res = QgsExpressionContext( *t1 );
|
||||
}
|
||||
sipReleaseType( t1, sipType_QgsExpressionContext, state );
|
||||
SIP_UNBLOCK_THREADS
|
||||
return res;
|
||||
} );
|
||||
|
||||
Py_END_ALLOW_THREADS
|
||||
%End
|
||||
|
||||
bool allowEvalErrors() const;
|
||||
%Docstring
|
||||
Allow accepting expressions with evaluation errors. This can be useful when we are not able to
|
||||
|
@ -147,7 +147,7 @@ QgsMeshLayerElevationProperties *QgsMeshLayerElevationProperties::clone() const
|
||||
return res.release();
|
||||
}
|
||||
|
||||
bool QgsMeshLayerElevationProperties::isVisibleInZRange( const QgsDoubleRange &, QgsMapLayer * ) const
|
||||
bool QgsMeshLayerElevationProperties::isVisibleInZRange( const QgsDoubleRange &range, QgsMapLayer * ) const
|
||||
{
|
||||
switch ( mMode )
|
||||
{
|
||||
|
@ -617,6 +617,11 @@ bool QgsExpressionBuilderWidget::isExpressionValid()
|
||||
return mExpressionValid;
|
||||
}
|
||||
|
||||
void QgsExpressionBuilderWidget::setCustomPreviewGenerator( const QString &label, const QList<QPair<QString, QVariant> > &choices, const std::function<QgsExpressionContext( const QVariant & )> &previewContextGenerator )
|
||||
{
|
||||
mExpressionPreviewWidget->setCustomPreviewGenerator( label, choices, previewContextGenerator );
|
||||
}
|
||||
|
||||
void QgsExpressionBuilderWidget::saveToRecent( const QString &collection )
|
||||
{
|
||||
mExpressionTreeView->saveToRecent( expressionText(), collection );
|
||||
|
@ -151,6 +151,63 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp
|
||||
//! Returns if the expression is valid
|
||||
bool isExpressionValid();
|
||||
|
||||
#ifndef SIP_RUN
|
||||
|
||||
/**
|
||||
* Sets the widget to run using a custom preview generator.
|
||||
*
|
||||
* In this mode, the widget will call a callback function to generate a new QgsExpressionContext
|
||||
* as the previewed object changes. This can be used to provide custom preview values for different
|
||||
* objects (i.e. for objects which aren't vector layer features).
|
||||
*
|
||||
* \param label The label to display for the combo box presenting choices of objects. This should be a representative name, eg "Band" if the widget is showing choices of raster layer bands
|
||||
* \param choices A list of choices to present to the user. Each choice is a pair of a human-readable label and a QVariant representing the object to preview.
|
||||
* \param previewContextGenerator A function which takes a QVariant representing the object to preview, and returns a QgsExpressionContext to use for previewing the object.
|
||||
*
|
||||
* \since QGIS 3.38
|
||||
*/
|
||||
void setCustomPreviewGenerator( const QString &label, const QList< QPair< QString, QVariant > > &choices, const std::function< QgsExpressionContext( const QVariant & ) > &previewContextGenerator );
|
||||
#else
|
||||
|
||||
/**
|
||||
* Sets the widget to run using a custom preview generator.
|
||||
*
|
||||
* In this mode, the widget will call a callback function to generate a new QgsExpressionContext
|
||||
* as the previewed object changes. This can be used to provide custom preview values for different
|
||||
* objects (i.e. for objects which aren't vector layer features).
|
||||
*
|
||||
* \param label The label to display for the combo box presenting choices of objects. This should be a representative name, eg "Band" if the widget is showing choices of raster layer bands
|
||||
* \param choices A list of choices to present to the user. Each choice is a pair of a human-readable label and a QVariant representing the object to preview.
|
||||
* \param previewContextGenerator A function which takes a QVariant representing the object to preview, and returns a QgsExpressionContext to use for previewing the object.
|
||||
*
|
||||
* \since QGIS 3.38
|
||||
*/
|
||||
void setCustomPreviewGenerator( const QString &label, const QList< QPair< QString, QVariant > > &choices, SIP_PYCALLABLE );
|
||||
% MethodCode
|
||||
Py_XINCREF( a2 );
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
sipCpp->setCustomPreviewGenerator( *a0, *a1, [a2]( const QVariant &value )->QgsExpressionContext
|
||||
{
|
||||
QgsExpressionContext res;
|
||||
SIP_BLOCK_THREADS
|
||||
PyObject *s = sipCallMethod( NULL, a2, "D", &value, sipType_QVariant, NULL );
|
||||
int state;
|
||||
int sipIsError = 0;
|
||||
QgsExpressionContext *t1 = reinterpret_cast<QgsExpressionContext *>( sipConvertToType( s, sipType_QgsExpressionContext, 0, SIP_NOT_NONE, &state, &sipIsError ) );
|
||||
if ( sipIsError == 0 )
|
||||
{
|
||||
res = QgsExpressionContext( *t1 );
|
||||
}
|
||||
sipReleaseType( t1, sipType_QgsExpressionContext, state );
|
||||
SIP_UNBLOCK_THREADS
|
||||
return res;
|
||||
} );
|
||||
|
||||
Py_END_ALLOW_THREADS
|
||||
% End
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Adds the current expression to the given \a collection.
|
||||
* By default it is saved to the collection "generic".
|
||||
|
@ -33,10 +33,23 @@ QgsExpressionPreviewWidget::QgsExpressionPreviewWidget( QWidget *parent )
|
||||
mCopyPreviewAction = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditCopy.svg" ) ), tr( "Copy Expression Value" ), this );
|
||||
mPreviewLabel->addAction( mCopyPreviewAction );
|
||||
mFeaturePickerWidget->setShowBrowserButtons( true );
|
||||
mStackedWidget->setSizeMode( QgsStackedWidget::SizeMode::CurrentPageOnly );
|
||||
mStackedWidget->setCurrentWidget( mPageFeaturePicker );
|
||||
|
||||
mCustomButtonNext->setEnabled( false );
|
||||
mCustomButtonPrev->setEnabled( false );
|
||||
connect( mFeaturePickerWidget, &QgsFeaturePickerWidget::featureChanged, this, &QgsExpressionPreviewWidget::setCurrentFeature );
|
||||
connect( mCustomComboBox, qOverload< int >( &QComboBox::currentIndexChanged ), this, &QgsExpressionPreviewWidget::setCustomChoice );
|
||||
connect( mPreviewLabel, &QLabel::linkActivated, this, &QgsExpressionPreviewWidget::linkActivated );
|
||||
connect( mCopyPreviewAction, &QAction::triggered, this, &QgsExpressionPreviewWidget::copyFullExpressionValue );
|
||||
connect( mCustomButtonPrev, &QToolButton::clicked, this, [this]
|
||||
{
|
||||
mCustomComboBox->setCurrentIndex( std::max( 0, mCustomComboBox->currentIndex() - 1 ) );
|
||||
} );
|
||||
connect( mCustomButtonNext, &QToolButton::clicked, this, [this]
|
||||
{
|
||||
mCustomComboBox->setCurrentIndex( std::min( mCustomComboBox->count() - 1, mCustomComboBox->currentIndex() + 1 ) );
|
||||
} );
|
||||
}
|
||||
|
||||
void QgsExpressionPreviewWidget::setLayer( QgsVectorLayer *layer )
|
||||
@ -48,6 +61,21 @@ void QgsExpressionPreviewWidget::setLayer( QgsVectorLayer *layer )
|
||||
}
|
||||
}
|
||||
|
||||
void QgsExpressionPreviewWidget::setCustomPreviewGenerator( const QString &label, const QList<QPair<QString, QVariant> > &choices, const std::function< QgsExpressionContext( const QVariant & ) > &previewContextGenerator )
|
||||
{
|
||||
mCustomPreviewGeneratorFunction = previewContextGenerator;
|
||||
mStackedWidget->setCurrentWidget( mPageCustomPicker );
|
||||
mCustomLabel->setText( label );
|
||||
mCustomComboBox->blockSignals( true );
|
||||
mCustomComboBox->clear();
|
||||
for ( const auto &choice : choices )
|
||||
{
|
||||
mCustomComboBox->addItem( choice.first, choice.second );
|
||||
}
|
||||
mCustomComboBox->blockSignals( false );
|
||||
setCustomChoice( 0 );
|
||||
}
|
||||
|
||||
void QgsExpressionPreviewWidget::setExpressionText( const QString &expression )
|
||||
{
|
||||
if ( expression != mExpressionText )
|
||||
@ -198,6 +226,11 @@ bool QgsExpressionPreviewWidget::parserError() const
|
||||
return mParserError;
|
||||
}
|
||||
|
||||
QString QgsExpressionPreviewWidget::currentPreviewText() const
|
||||
{
|
||||
return mPreviewLabel->text();
|
||||
}
|
||||
|
||||
void QgsExpressionPreviewWidget::setEvalError( bool evalError )
|
||||
{
|
||||
if ( evalError == mEvalError )
|
||||
@ -220,3 +253,15 @@ void QgsExpressionPreviewWidget::copyFullExpressionValue()
|
||||
QgsDebugMsgLevel( QStringLiteral( "set clipboard: %1" ).arg( copiedValue ), 4 );
|
||||
clipboard->setText( copiedValue );
|
||||
}
|
||||
|
||||
void QgsExpressionPreviewWidget::setCustomChoice( int )
|
||||
{
|
||||
const QVariant selectedValue = mCustomComboBox->currentData();
|
||||
|
||||
mCustomButtonPrev->setEnabled( mCustomComboBox->currentIndex() > 0 && mCustomComboBox->count() > 0 );
|
||||
mCustomButtonNext->setEnabled( mCustomComboBox->currentIndex() < ( mCustomComboBox->count() - 1 ) && mCustomComboBox->count() > 0 );
|
||||
|
||||
mExpressionContext = mCustomPreviewGeneratorFunction( selectedValue );
|
||||
|
||||
refreshPreview();
|
||||
}
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include "qgsexpression.h"
|
||||
#include "qgsexpressioncontext.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
class QAction;
|
||||
class QgsVectorLayer;
|
||||
|
||||
@ -44,6 +46,62 @@ class GUI_EXPORT QgsExpressionPreviewWidget : public QWidget, private Ui::QgsExp
|
||||
//! Sets the layer used in the preview
|
||||
void setLayer( QgsVectorLayer *layer );
|
||||
|
||||
#ifndef SIP_RUN
|
||||
|
||||
/**
|
||||
* Sets the widget to run using a custom preview generator.
|
||||
*
|
||||
* In this mode, the widget will call a callback function to generate a new QgsExpressionContext
|
||||
* as the previewed object changes. This can be used to provide custom preview values for different
|
||||
* objects (i.e. for objects which aren't vector layer features).
|
||||
*
|
||||
* \param label The label to display for the combo box presenting choices of objects. This should be a representative name, eg "Band" if the widget is showing choices of raster layer bands
|
||||
* \param choices A list of choices to present to the user. Each choice is a pair of a human-readable label and a QVariant representing the object to preview.
|
||||
* \param previewContextGenerator A function which takes a QVariant representing the object to preview, and returns a QgsExpressionContext to use for previewing the object.
|
||||
*
|
||||
* \since QGIS 3.38
|
||||
*/
|
||||
void setCustomPreviewGenerator( const QString &label, const QList< QPair< QString, QVariant > > &choices, const std::function< QgsExpressionContext( const QVariant & ) > &previewContextGenerator );
|
||||
#else
|
||||
|
||||
/**
|
||||
* Sets the widget to run using a custom preview generator.
|
||||
*
|
||||
* In this mode, the widget will call a callback function to generate a new QgsExpressionContext
|
||||
* as the previewed object changes. This can be used to provide custom preview values for different
|
||||
* objects (i.e. for objects which aren't vector layer features).
|
||||
*
|
||||
* \param label The label to display for the combo box presenting choices of objects. This should be a representative name, eg "Band" if the widget is showing choices of raster layer bands
|
||||
* \param choices A list of choices to present to the user. Each choice is a pair of a human-readable label and a QVariant representing the object to preview.
|
||||
* \param previewContextGenerator A function which takes a QVariant representing the object to preview, and returns a QgsExpressionContext to use for previewing the object.
|
||||
*
|
||||
* \since QGIS 3.38
|
||||
*/
|
||||
void setCustomPreviewGenerator( const QString &label, const QList< QPair< QString, QVariant > > &choices, SIP_PYCALLABLE );
|
||||
% MethodCode
|
||||
Py_XINCREF( a2 );
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
sipCpp->setCustomPreviewGenerator( *a0, *a1, [a2]( const QVariant &value )->QgsExpressionContext
|
||||
{
|
||||
QgsExpressionContext res;
|
||||
SIP_BLOCK_THREADS
|
||||
PyObject *s = sipCallMethod( NULL, a2, "D", &value, sipType_QVariant, NULL );
|
||||
int state;
|
||||
int sipIsError = 0;
|
||||
QgsExpressionContext *t1 = reinterpret_cast<QgsExpressionContext *>( sipConvertToType( s, sipType_QgsExpressionContext, 0, SIP_NOT_NONE, &state, &sipIsError ) );
|
||||
if ( sipIsError == 0 )
|
||||
{
|
||||
res = QgsExpressionContext( *t1 );
|
||||
}
|
||||
sipReleaseType( t1, sipType_QgsExpressionContext, state );
|
||||
SIP_UNBLOCK_THREADS
|
||||
return res;
|
||||
} );
|
||||
|
||||
Py_END_ALLOW_THREADS
|
||||
% End
|
||||
#endif
|
||||
|
||||
//! Sets the expression
|
||||
void setExpressionText( const QString &expression );
|
||||
|
||||
@ -82,6 +140,13 @@ class GUI_EXPORT QgsExpressionPreviewWidget : public QWidget, private Ui::QgsExp
|
||||
//! Returns the expression parser errors
|
||||
QList<QgsExpression::ParserError> parserErrors() const {return mExpression.parserErrors();}
|
||||
|
||||
/**
|
||||
* Returns the current expression result preview text.
|
||||
*
|
||||
* \since QGIS 3.38
|
||||
*/
|
||||
QString currentPreviewText() const;
|
||||
|
||||
signals:
|
||||
|
||||
/**
|
||||
@ -117,6 +182,7 @@ class GUI_EXPORT QgsExpressionPreviewWidget : public QWidget, private Ui::QgsExp
|
||||
void setEvalError( bool evalError );
|
||||
void setParserError( bool parserError );
|
||||
void copyFullExpressionValue();
|
||||
void setCustomChoice( int );
|
||||
|
||||
private:
|
||||
void setExpressionToolTip( const QString &toolTip );
|
||||
@ -132,6 +198,8 @@ class GUI_EXPORT QgsExpressionPreviewWidget : public QWidget, private Ui::QgsExp
|
||||
QString mExpressionText;
|
||||
QgsExpression mExpression;
|
||||
QAction *mCopyPreviewAction = nullptr;
|
||||
|
||||
std::function< QgsExpressionContext( const QVariant & ) > mCustomPreviewGeneratorFunction;
|
||||
};
|
||||
|
||||
#endif // QGSEXPRESSIONPREVIEWWIDGET_H
|
||||
|
@ -162,6 +162,13 @@ void QgsFieldExpressionWidget::registerExpressionContextGenerator( const QgsExpr
|
||||
mExpressionContextGenerator = generator;
|
||||
}
|
||||
|
||||
void QgsFieldExpressionWidget::setCustomPreviewGenerator( const QString &label, const QList<QPair<QString, QVariant> > &choices, const std::function<QgsExpressionContext( const QVariant & )> &previewContextGenerator )
|
||||
{
|
||||
mCustomPreviewLabel = label;
|
||||
mCustomChoices = choices;
|
||||
mPreviewContextGenerator = previewContextGenerator;
|
||||
}
|
||||
|
||||
void QgsFieldExpressionWidget::setLayer( QgsMapLayer *layer )
|
||||
{
|
||||
QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer );
|
||||
@ -243,6 +250,11 @@ void QgsFieldExpressionWidget::editExpression()
|
||||
dlg.setWindowTitle( mExpressionDialogTitle );
|
||||
dlg.setAllowEvalErrors( mAllowEvalErrors );
|
||||
|
||||
if ( !mCustomChoices.isEmpty() )
|
||||
{
|
||||
dlg.expressionBuilder()->setCustomPreviewGenerator( mCustomPreviewLabel, mCustomChoices, mPreviewContextGenerator );
|
||||
}
|
||||
|
||||
if ( !vl )
|
||||
dlg.expressionBuilder()->expressionTree()->loadFieldNames( mFieldProxyModel->sourceFieldModel()->fields() );
|
||||
|
||||
|
@ -152,6 +152,62 @@ class GUI_EXPORT QgsFieldExpressionWidget : public QWidget
|
||||
*/
|
||||
void registerExpressionContextGenerator( const QgsExpressionContextGenerator *generator );
|
||||
|
||||
#ifndef SIP_RUN
|
||||
|
||||
/**
|
||||
* Sets the widget to run using a custom preview generator.
|
||||
*
|
||||
* In this mode, the widget will call a callback function to generate a new QgsExpressionContext
|
||||
* as the previewed object changes. This can be used to provide custom preview values for different
|
||||
* objects (i.e. for objects which aren't vector layer features).
|
||||
*
|
||||
* \param label The label to display for the combo box presenting choices of objects. This should be a representative name, eg "Band" if the widget is showing choices of raster layer bands
|
||||
* \param choices A list of choices to present to the user. Each choice is a pair of a human-readable label and a QVariant representing the object to preview.
|
||||
* \param previewContextGenerator A function which takes a QVariant representing the object to preview, and returns a QgsExpressionContext to use for previewing the object.
|
||||
*
|
||||
* \since QGIS 3.38
|
||||
*/
|
||||
void setCustomPreviewGenerator( const QString &label, const QList< QPair< QString, QVariant > > &choices, const std::function< QgsExpressionContext( const QVariant & ) > &previewContextGenerator );
|
||||
#else
|
||||
|
||||
/**
|
||||
* Sets the widget to run using a custom preview generator.
|
||||
*
|
||||
* In this mode, the widget will call a callback function to generate a new QgsExpressionContext
|
||||
* as the previewed object changes. This can be used to provide custom preview values for different
|
||||
* objects (i.e. for objects which aren't vector layer features).
|
||||
*
|
||||
* \param label The label to display for the combo box presenting choices of objects. This should be a representative name, eg "Band" if the widget is showing choices of raster layer bands
|
||||
* \param choices A list of choices to present to the user. Each choice is a pair of a human-readable label and a QVariant representing the object to preview.
|
||||
* \param previewContextGenerator A function which takes a QVariant representing the object to preview, and returns a QgsExpressionContext to use for previewing the object.
|
||||
*
|
||||
* \since QGIS 3.38
|
||||
*/
|
||||
void setCustomPreviewGenerator( const QString &label, const QList< QPair< QString, QVariant > > &choices, SIP_PYCALLABLE );
|
||||
% MethodCode
|
||||
Py_XINCREF( a2 );
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
sipCpp->setCustomPreviewGenerator( *a0, *a1, [a2]( const QVariant &value )->QgsExpressionContext
|
||||
{
|
||||
QgsExpressionContext res;
|
||||
SIP_BLOCK_THREADS
|
||||
PyObject *s = sipCallMethod( NULL, a2, "D", &value, sipType_QVariant, NULL );
|
||||
int state;
|
||||
int sipIsError = 0;
|
||||
QgsExpressionContext *t1 = reinterpret_cast<QgsExpressionContext *>( sipConvertToType( s, sipType_QgsExpressionContext, 0, SIP_NOT_NONE, &state, &sipIsError ) );
|
||||
if ( sipIsError == 0 )
|
||||
{
|
||||
res = QgsExpressionContext( *t1 );
|
||||
}
|
||||
sipReleaseType( t1, sipType_QgsExpressionContext, state );
|
||||
SIP_UNBLOCK_THREADS
|
||||
return res;
|
||||
} );
|
||||
|
||||
Py_END_ALLOW_THREADS
|
||||
% End
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Allow accepting expressions with evaluation errors. This can be useful when we are not able to
|
||||
* provide an expression context of which we are sure it's completely populated.
|
||||
@ -275,6 +331,10 @@ class GUI_EXPORT QgsFieldExpressionWidget : public QWidget
|
||||
QString mBackupExpression;
|
||||
bool mAllowEvalErrors = false;
|
||||
|
||||
QString mCustomPreviewLabel;
|
||||
QList< QPair< QString, QVariant > > mCustomChoices;
|
||||
std::function< QgsExpressionContext( const QVariant & ) > mPreviewContextGenerator;
|
||||
|
||||
friend class TestQgsFieldExpressionWidget;
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>67</height>
|
||||
<height>50</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -65,27 +65,112 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
<widget class="QgsStackedWidget" name="mStackedWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="mPageFeaturePicker">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Select the feature to use for the output preview</string>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Feature</string>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QgsFeaturePickerWidget" name="mFeaturePickerWidget"/>
|
||||
</item>
|
||||
</layout>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Select the feature to use for the output preview</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Feature</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QgsFeaturePickerWidget" name="mFeaturePickerWidget"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="mPageCustomPicker">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1,0,0">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="mCustomLabel">
|
||||
<property name="text">
|
||||
<string>Result</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="mCustomComboBox"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="mCustomButtonPrev">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../images/images.qrc">
|
||||
<normaloff>:/images/themes/default/mActionArrowLeft.svg</normaloff>:/images/themes/default/mActionArrowLeft.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="mCustomButtonNext">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../images/images.qrc">
|
||||
<normaloff>:/images/themes/default/mActionArrowRight.svg</normaloff>:/images/themes/default/mActionArrowRight.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -95,7 +180,15 @@
|
||||
<extends>QComboBox</extends>
|
||||
<header>qgsfeaturepickerwidget.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsStackedWidget</class>
|
||||
<extends>QStackedWidget</extends>
|
||||
<header>qgsstackedwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<resources>
|
||||
<include location="../../images/images.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
@ -68,6 +68,7 @@ ADD_PYTHON_TEST(PyQgsEllipsoidUtils test_qgsellipsoidutils.py)
|
||||
ADD_PYTHON_TEST(PyQgsEmbeddedSymbolRenderer test_qgsembeddedsymbolrenderer.py)
|
||||
ADD_PYTHON_TEST(PyQgsExifTools test_qgsexiftools.py)
|
||||
ADD_PYTHON_TEST(PyQgsExpression test_qgsexpression.py)
|
||||
ADD_PYTHON_TEST(PyQgsExpressionPreviewWidget test_qgsexpressionpreviewwidget.py)
|
||||
ADD_PYTHON_TEST(PyQgsExternalStorageWebDav test_qgsexternalstorage_webdav.py)
|
||||
ADD_PYTHON_TEST(PyQgsExternalStorageAwsS3 test_qgsexternalstorage_awss3.py)
|
||||
ADD_PYTHON_TEST(PyQgsFeature test_qgsfeature.py)
|
||||
|
67
tests/src/python/test_qgsexpressionpreviewwidget.py
Normal file
67
tests/src/python/test_qgsexpressionpreviewwidget.py
Normal file
@ -0,0 +1,67 @@
|
||||
"""QGIS Unit tests for QgsExpressionPreviewWidget
|
||||
|
||||
.. note:: This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
"""
|
||||
|
||||
from qgis.PyQt.QtWidgets import QToolButton
|
||||
from qgis.gui import QgsExpressionPreviewWidget
|
||||
from qgis.core import (
|
||||
QgsExpressionContext,
|
||||
QgsExpressionContextScope
|
||||
)
|
||||
import unittest
|
||||
from qgis.testing import start_app, QgisTestCase
|
||||
|
||||
start_app()
|
||||
|
||||
|
||||
class TestQgsExpressionPreviewWidget(QgisTestCase):
|
||||
|
||||
def test_custom_mode(self):
|
||||
"""
|
||||
Test using a custom preview generator with the widget
|
||||
"""
|
||||
def make_context(value):
|
||||
res = QgsExpressionContext()
|
||||
scope = QgsExpressionContextScope()
|
||||
scope.setVariable('test', value)
|
||||
scope.setVariable('test2', value * 2)
|
||||
res.appendScope(scope)
|
||||
return res
|
||||
|
||||
w = QgsExpressionPreviewWidget()
|
||||
w.setCustomPreviewGenerator('Band',
|
||||
[['Band 1', 1], ['Band 2', 2], ['Band 3', 3]],
|
||||
make_context)
|
||||
w.setExpressionText("@test * 5")
|
||||
self.assertEqual(w.currentPreviewText(), '5')
|
||||
w.setExpressionText("@test2 * 5")
|
||||
self.assertEqual(w.currentPreviewText(), '10')
|
||||
|
||||
next_button = w.findChild(QToolButton, 'mCustomButtonNext')
|
||||
prev_button = w.findChild(QToolButton, 'mCustomButtonPrev')
|
||||
self.assertFalse(prev_button.isEnabled())
|
||||
self.assertTrue(next_button.isEnabled())
|
||||
next_button.click()
|
||||
self.assertEqual(w.currentPreviewText(), '20')
|
||||
self.assertTrue(prev_button.isEnabled())
|
||||
self.assertTrue(next_button.isEnabled())
|
||||
next_button.click()
|
||||
self.assertEqual(w.currentPreviewText(), '30')
|
||||
self.assertTrue(prev_button.isEnabled())
|
||||
self.assertFalse(next_button.isEnabled())
|
||||
prev_button.click()
|
||||
self.assertEqual(w.currentPreviewText(), '20')
|
||||
self.assertTrue(prev_button.isEnabled())
|
||||
self.assertTrue(next_button.isEnabled())
|
||||
prev_button.click()
|
||||
self.assertEqual(w.currentPreviewText(), '10')
|
||||
self.assertFalse(prev_button.isEnabled())
|
||||
self.assertTrue(next_button.isEnabled())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
x
Reference in New Issue
Block a user