QGIS API Documentation 4.1.0-Master (376402f9aeb)
Loading...
Searching...
No Matches
qgsqmlwidgetwrapper.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsqmlwidgetwrapper.cpp
3
4 ---------------------
5 begin : 25.6.2018
6 copyright : (C) 2018 by Matthias Kuhn
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17#include "qgsqmlwidgetwrapper.h"
18
19#include "qgsattributeform.h"
21#include "qgsmessagelog.h"
23
24#include <QQmlContext>
25#include <QQmlEngine>
26#include <QString>
27#include <QUrl>
28
29#include "moc_qgsqmlwidgetwrapper.cpp"
30
31using namespace Qt::StringLiterals;
32
34 : QgsWidgetWrapper( layer, editor, parent )
35{
36 connect( this, &QgsWidgetWrapper::contextChanged, this, &QgsQmlWidgetWrapper::setQmlContext );
37}
38
40{
41 return true;
42}
43
44QWidget *QgsQmlWidgetWrapper::createWidget( QWidget *parent )
45{
46 QgsAttributeForm *form = qobject_cast<QgsAttributeForm *>( parent );
47
48 if ( form )
49 {
50 mFormFeature = form->feature();
51 connect( form, &QgsAttributeForm::widgetValueChanged, this, [this]( const QString &attribute, const QVariant &newValue, bool attributeChanged ) {
52 if ( attributeChanged )
53 {
54 if ( mRequiresFormScope )
55 {
56 mFormFeature.setAttribute( attribute, newValue );
57 setQmlContext();
58 }
59 }
60 } );
61 }
62
63 return new QQuickWidget( parent );
64}
65
66void QgsQmlWidgetWrapper::initWidget( QWidget *editor )
67{
68 mWidget = qobject_cast<QQuickWidget *>( editor );
69
70 if ( !mWidget )
71 return;
72
73
74 if ( !mQmlFile.open() )
75 {
76 QgsMessageLog::logMessage( tr( "Failed to open temporary QML file" ) );
77 return;
78 }
79
80 QUrl sourceUrl = QUrl::fromLocalFile( mQmlFile.fileName() );
81 sourceUrl.setQuery( u"t=%1"_s.arg( QDateTime::currentSecsSinceEpoch() ) );
82 mWidget->setSource( sourceUrl );
83
84 mQmlFile.close();
85}
86
87
89{
90 if ( !mWidget )
91 return;
92
93 mWidget->engine()->clearComponentCache();
94
95 initWidget( mWidget );
96}
97
98void QgsQmlWidgetWrapper::setQmlCode( const QString &qmlCode )
99{
100 if ( mQmlCode == qmlCode )
101 {
102 return;
103 }
104
105 mQmlCode = qmlCode;
106
107 bool ok = false;
108 const thread_local QRegularExpression
109 expRe( QStringLiteral( R"re(expression.evaluate\s*\‍(\s*"(.*)"\))re" ), QRegularExpression::PatternOption::MultilineOption | QRegularExpression::PatternOption::DotMatchesEverythingOption );
110 QRegularExpressionMatchIterator matchIt = expRe.globalMatch( mQmlCode );
111 while ( !ok && matchIt.hasNext() )
112 {
113 const QRegularExpressionMatch match = matchIt.next();
114 const QgsExpression exp = match.captured( 1 );
116 }
117 mRequiresFormScope = ok;
118
119 if ( !mQmlFile.open() )
120 {
121 QgsMessageLog::logMessage( tr( "Failed to open temporary QML file" ) );
122 return;
123 }
124
125 mQmlFile.resize( 0 );
126 mQmlFile.write( mQmlCode.toUtf8() );
127 mQmlFile.close();
128}
129
130void QgsQmlWidgetWrapper::setQmlContext()
131{
132 if ( !mWidget )
133 return;
134
135 const QgsAttributeEditorContext attributecontext = context();
136 QgsExpressionContext expressionContext = layer()->createExpressionContext();
137 expressionContext << QgsExpressionContextUtils::formScope( mFormFeature, attributecontext.attributeFormModeString() );
138 if ( attributecontext.parentFormFeature().isValid() )
139 {
140 expressionContext << QgsExpressionContextUtils::parentFormScope( attributecontext.parentFormFeature() );
141 }
142
143 expressionContext.setFeature( mFeature );
144
145 QmlExpression *qmlExpression = new QmlExpression();
146 qmlExpression->setExpressionContext( expressionContext );
147
148 mWidget->rootContext()->setContextProperty( "expression", qmlExpression );
149}
150
152{
153 if ( !mWidget )
154 return;
155
156 mFeature = feature;
157 mFormFeature = feature;
158
159 setQmlContext();
160}
161
163void QmlExpression::setExpressionContext( const QgsExpressionContext &context )
164{
165 mExpressionContext = context;
166}
167
168QVariant QmlExpression::evaluate( const QString &expression ) const
169{
170 QgsExpression exp = QgsExpression( expression );
171 exp.prepare( &mExpressionContext );
172 return exp.evaluate( &mExpressionContext );
173}
Contains context information for attribute editor widgets.
QString attributeFormModeString() const
Returns given attributeFormMode as string.
QgsFeature parentFormFeature() const
Returns the feature of the currently edited parent form in its actual state.
The attribute form widget for vector layer features.
void widgetValueChanged(const QString &attribute, const QVariant &value, bool attributeChanged)
Notifies about changes of attributes.
const QgsFeature & feature() const
Returns feature of attribute form.
static QgsExpressionContextScope * parentFormScope(const QgsFeature &formFeature=QgsFeature(), const QString &formMode=QString())
Creates a new scope which contains functions and variables from the current parent attribute form/tab...
static QgsExpressionContextScope * formScope(const QgsFeature &formFeature=QgsFeature(), const QString &formMode=QString())
Creates a new scope which contains functions and variables from the current attribute form/table form...
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
QVariant evaluate()
Evaluate the feature and return the result.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
bool isValid() const
Returns the validity of this feature.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
QWidget * createWidget(QWidget *parent) override
This method should create a new widget with the provided parent.
void initWidget(QWidget *editor) override
This method should initialize the editor widget with runtime data.
bool valid() const override
Returns true if the widget has been properly initialized.
void setFeature(const QgsFeature &feature) override
void reinitWidget()
Clears the content and makes new initialization.
void setQmlCode(const QString &qmlCode)
writes the qmlCode into a temporary file
QgsQmlWidgetWrapper(QgsVectorLayer *layer, QWidget *editor, QWidget *parent)
Create a qml widget wrapper.
static bool expressionRequiresFormScope(const QString &expression)
Check if the expression requires a form scope (i.e.
Represents a vector layer which manages a vector based dataset.
QgsExpressionContext createExpressionContext() const final
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void contextChanged()
Signal when QgsAttributeEditorContext mContext changed.
QgsWidgetWrapper(QgsVectorLayer *vl, QWidget *editor=nullptr, QWidget *parent=nullptr)
Create a new widget wrapper.
const QgsAttributeEditorContext & context() const
Returns information about the context in which this widget is shown.
QgsVectorLayer * layer() const
Returns the vector layer associated with the widget.