Merge pull request #55146 from nirvn/qml_responsive

[editor widgets] Fix QML editor widget not responsive to attribute changes
This commit is contained in:
Mathieu Pellerin 2023-11-25 07:35:39 +07:00 committed by GitHub
commit 0ce0b78295
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 82 additions and 20 deletions

View File

@ -7,6 +7,7 @@
************************************************************************/
class QgsQmlWidgetWrapper : QgsWidgetWrapper
{
%Docstring(signature="appended")

View File

@ -19,6 +19,7 @@
#include "qgswebframe.h"
#include "qgsvaluerelationfieldformatter.h"
#include "qgsattributeform.h"
#include <QScreen>
QgsHtmlWidgetWrapper::QgsHtmlWidgetWrapper( QgsVectorLayer *layer, QWidget *editor, QWidget *parent )
@ -34,7 +35,6 @@ bool QgsHtmlWidgetWrapper::valid() const
QWidget *QgsHtmlWidgetWrapper::createWidget( QWidget *parent )
{
QgsAttributeForm *form = qobject_cast<QgsAttributeForm *>( parent );
if ( form )
@ -44,9 +44,7 @@ QWidget *QgsHtmlWidgetWrapper::createWidget( QWidget *parent )
{
if ( attributeChanged )
{
const thread_local QRegularExpression expRe { QStringLiteral( R"re(expression.evaluate\s*\(\s*"(.*)"\))re" ), QRegularExpression::PatternOption::MultilineOption | QRegularExpression::PatternOption::DotMatchesEverythingOption };
const QRegularExpressionMatch match { expRe.match( mHtmlCode ) };
if ( match.hasMatch() && QgsValueRelationFieldFormatter::expressionRequiresFormScope( match.captured( 1 ) ) )
if ( mRequiresFormScope )
{
mFormFeature.setAttribute( attribute, newValue );
setHtmlContext();
@ -121,6 +119,18 @@ void QgsHtmlWidgetWrapper::checkGeometryNeeds()
void QgsHtmlWidgetWrapper::setHtmlCode( const QString &htmlCode )
{
mHtmlCode = htmlCode;
bool ok = false;
const thread_local QRegularExpression expRe( QStringLiteral( R"re(expression.evaluate\s*\(\s*"(.*)"\))re" ), QRegularExpression::PatternOption::MultilineOption | QRegularExpression::PatternOption::DotMatchesEverythingOption );
QRegularExpressionMatchIterator matchIt = expRe.globalMatch( mHtmlCode );
while ( !ok && matchIt.hasNext() )
{
const QRegularExpressionMatch match = matchIt.next();
const QgsExpression exp = match.captured( 1 );
ok = QgsValueRelationFieldFormatter::expressionRequiresFormScope( exp );
}
mRequiresFormScope = ok;
checkGeometryNeeds();
}

View File

@ -79,6 +79,7 @@ class GUI_EXPORT QgsHtmlWidgetWrapper : public QgsWidgetWrapper
QgsFeature mFeature;
bool mNeedsGeometry = false;
QgsFeature mFormFeature;
bool mRequiresFormScope = false;
friend class TestQgsHtmlWidgetWrapper;
};

View File

@ -13,12 +13,13 @@
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsqmlwidgetwrapper.h"
#include "qgsattributeform.h"
#include "qgsmessagelog.h"
#include "qgsexpressioncontextutils.h"
#include "qgsvaluerelationfieldformatter.h"
#include <QtQuickWidgets/QQuickWidget>
#include <QQuickWidget>
#include <QQmlContext>
#include <QQmlEngine>
#include <QUrl>
@ -36,6 +37,24 @@ bool QgsQmlWidgetWrapper::valid() const
QWidget *QgsQmlWidgetWrapper::createWidget( QWidget *parent )
{
QgsAttributeForm *form = qobject_cast<QgsAttributeForm *>( parent );
if ( form )
{
mFormFeature = form->feature();
connect( form, &QgsAttributeForm::widgetValueChanged, this, [ = ]( const QString & attribute, const QVariant & newValue, bool attributeChanged )
{
if ( attributeChanged )
{
if ( mRequiresFormScope )
{
mFormFeature.setAttribute( attribute, newValue );
setQmlContext();
}
}
} );
}
return new QQuickWidget( parent );
}
@ -71,6 +90,24 @@ void QgsQmlWidgetWrapper::reinitWidget( )
void QgsQmlWidgetWrapper::setQmlCode( const QString &qmlCode )
{
if ( mQmlCode == qmlCode )
{
return;
}
mQmlCode = qmlCode;
bool ok = false;
const thread_local QRegularExpression expRe( QStringLiteral( R"re(expression.evaluate\s*\(\s*"(.*)"\))re" ), QRegularExpression::PatternOption::MultilineOption | QRegularExpression::PatternOption::DotMatchesEverythingOption );
QRegularExpressionMatchIterator matchIt = expRe.globalMatch( mQmlCode );
while ( !ok && matchIt.hasNext() )
{
const QRegularExpressionMatch match = matchIt.next();
const QgsExpression exp = match.captured( 1 );
ok = QgsValueRelationFieldFormatter::expressionRequiresFormScope( exp );
}
mRequiresFormScope = ok;
if ( !mQmlFile.open() )
{
QgsMessageLog::logMessage( tr( "Failed to open temporary QML file" ) );
@ -78,7 +115,7 @@ void QgsQmlWidgetWrapper::setQmlCode( const QString &qmlCode )
}
mQmlFile.resize( 0 );
mQmlFile.write( qmlCode.toUtf8() );
mQmlFile.write( mQmlCode.toUtf8() );
mQmlFile.close();
}
@ -90,7 +127,12 @@ void QgsQmlWidgetWrapper::setQmlContext( )
const QgsAttributeEditorContext attributecontext = context();
QgsExpressionContext expressionContext = layer()->createExpressionContext();
expressionContext << QgsExpressionContextUtils::formScope( mFeature, attributecontext.attributeFormModeString() );
expressionContext << QgsExpressionContextUtils::formScope( mFormFeature, attributecontext.attributeFormModeString() );
if ( attributecontext.parentFormFeature().isValid() )
{
expressionContext << QgsExpressionContextUtils::parentFormScope( attributecontext.parentFormFeature() );
}
expressionContext.setFeature( mFeature );
QmlExpression *qmlExpression = new QmlExpression();
@ -105,6 +147,7 @@ void QgsQmlWidgetWrapper::setFeature( const QgsFeature &feature )
return;
mFeature = feature;
mFormFeature = feature;
setQmlContext();
}

View File

@ -19,7 +19,8 @@
#include "qgswidgetwrapper.h"
#include "qgis_sip.h"
#include "qgis_gui.h"
#include <QtQuickWidgets/QQuickWidget>
#include <QQuickWidget>
/**
* \ingroup gui
@ -62,8 +63,11 @@ class GUI_EXPORT QgsQmlWidgetWrapper : public QgsWidgetWrapper
private:
QTemporaryFile mQmlFile;
QString mQmlCode;
QQuickWidget *mWidget = nullptr;
QgsFeature mFeature;
QgsFeature mFormFeature;
bool mRequiresFormScope = false;
};

View File

@ -42,17 +42,7 @@ QWidget *QgsTextWidgetWrapper::createWidget( QWidget *parent )
{
if ( attributeChanged )
{
bool ok { false };
const thread_local QRegularExpression sRegEx{ QStringLiteral( "\\[%(.*?)%\\]" ), QRegularExpression::MultilineOption | QRegularExpression::DotMatchesEverythingOption };
QRegularExpressionMatchIterator matchIt { sRegEx.globalMatch( mText ) };
while ( !ok && matchIt.hasNext() )
{
const QRegularExpressionMatch match { matchIt.next() };
const QgsExpression exp { match.captured( 1 ) };
ok = QgsValueRelationFieldFormatter::expressionRequiresFormScope( exp );
}
if ( ok )
if ( mRequiresFormScope )
{
mFormFeature.setAttribute( attribute, newValue );
updateTextContext();
@ -99,6 +89,18 @@ void QgsTextWidgetWrapper::reinitWidget( )
void QgsTextWidgetWrapper::setText( const QString &text )
{
mText = text;
bool ok = false;
const thread_local QRegularExpression sRegEx( QStringLiteral( "\\[%(.*?)%\\]" ), QRegularExpression::MultilineOption | QRegularExpression::DotMatchesEverythingOption );
QRegularExpressionMatchIterator matchIt = sRegEx.globalMatch( mText );
while ( !ok && matchIt.hasNext() )
{
const QRegularExpressionMatch match = matchIt.next();
const QgsExpression exp = match.captured( 1 );
ok = QgsValueRelationFieldFormatter::expressionRequiresFormScope( exp );
}
mRequiresFormScope = ok;
reinitWidget();
}

View File

@ -71,6 +71,7 @@ class GUI_EXPORT QgsTextWidgetWrapper : public QgsWidgetWrapper
QLabel *mWidget = nullptr;
QgsFeature mFeature;
QgsFeature mFormFeature;
bool mRequiresFormScope = false;
QgsExpressionContext mTextContext;
bool mNeedsGeometry = false;