QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
qgshtmlwidgetwrapper.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgshtmlwidgetwrapper.h
3
4 ---------------------
5 begin : 23.03.2019
6 copyright : (C) 2019 by Alessandro Pasotti
7 email : elpaso at itopen dot it
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
19#include "qgswebframe.h"
21#include "qgsattributeform.h"
22#include <QScreen>
23
24QgsHtmlWidgetWrapper::QgsHtmlWidgetWrapper( QgsVectorLayer *layer, QWidget *editor, QWidget *parent )
25 : QgsWidgetWrapper( layer, editor, parent )
26{
27 connect( this, &QgsWidgetWrapper::contextChanged, this, &QgsHtmlWidgetWrapper::setHtmlContext );
28}
29
31{
32 return true;
33}
34
35QWidget *QgsHtmlWidgetWrapper::createWidget( QWidget *parent )
36{
37
38 QgsAttributeForm *form = qobject_cast<QgsAttributeForm *>( parent );
39
40 if ( form )
41 {
42 mFormFeature = form->feature();
43 connect( form, &QgsAttributeForm::widgetValueChanged, this, [ = ]( const QString & attribute, const QVariant & newValue, bool attributeChanged )
44 {
45 if ( attributeChanged )
46 {
47 const thread_local QRegularExpression expRe { QStringLiteral( R"re(expression.evaluate\s*\‍(\s*"(.*)"\))re" ), QRegularExpression::PatternOption::MultilineOption | QRegularExpression::PatternOption::DotMatchesEverythingOption };
48 const QRegularExpressionMatch match { expRe.match( mHtmlCode ) };
49 if ( match.hasMatch() && QgsValueRelationFieldFormatter::expressionRequiresFormScope( match.captured( 1 ) ) )
50 {
51 mFormFeature.setAttribute( attribute, newValue );
52 setHtmlContext();
53 }
54 }
55 } );
56 }
57
58 return new QgsWebView( parent );
59}
60
61void QgsHtmlWidgetWrapper::initWidget( QWidget *editor )
62{
63 mWidget = qobject_cast<QgsWebView *>( editor );
64
65 if ( !mWidget )
66 return;
67
68 mWidget->setHtml( mHtmlCode.replace( "\n", " " ) );
69
70#ifdef WITH_QTWEBKIT
71
72 const int horizontalDpi = mWidget->logicalDpiX();
73
74 mWidget->setZoomFactor( horizontalDpi / 96.0 );
75
76 QWebPage *page = mWidget->page();
77 connect( page, &QWebPage::contentsChanged, this, &QgsHtmlWidgetWrapper::fixHeight, Qt::ConnectionType::UniqueConnection );
78 connect( page, &QWebPage::loadFinished, this, &QgsHtmlWidgetWrapper::fixHeight, Qt::ConnectionType::UniqueConnection );
79
80#endif
81
82 checkGeometryNeeds();
83}
84
86{
87 if ( !mWidget )
88 return;
89
90 initWidget( mWidget );
91}
92
93void QgsHtmlWidgetWrapper::checkGeometryNeeds()
94{
95 if ( !mWidget )
96 return;
97
98 // initialize a temporary QgsWebView to render HTML code and check if one evaluated expression
99 // needs geometry
100 QgsWebView webView;
101 NeedsGeometryEvaluator evaluator;
102
103 const QgsAttributeEditorContext attributecontext = context();
104 if ( QgsVectorLayer *vl = layer() )
105 {
106 const QgsExpressionContext expressionContext = vl->createExpressionContext();
107 evaluator.setExpressionContext( expressionContext );
108 }
109
110 auto frame = webView.page()->mainFrame();
111 connect( frame, &QWebFrame::javaScriptWindowObjectCleared, frame, [ frame, &evaluator ]
112 {
113 frame->addToJavaScriptWindowObject( QStringLiteral( "expression" ), &evaluator );
114 } );
115
116 webView.setHtml( mHtmlCode );
117
118 mNeedsGeometry = evaluator.needsGeometry();
119}
120
121void QgsHtmlWidgetWrapper::setHtmlCode( const QString &htmlCode )
122{
123 mHtmlCode = htmlCode;
124 checkGeometryNeeds();
125}
126
127void QgsHtmlWidgetWrapper::setHtmlContext( )
128{
129 if ( !mWidget )
130 return;
131
132 const QgsAttributeEditorContext attributecontext = context();
133 QgsExpressionContext expressionContext = layer()->createExpressionContext();
134 expressionContext << QgsExpressionContextUtils::formScope( mFormFeature, attributecontext.attributeFormModeString() );
135 if ( attributecontext.parentFormFeature().isValid() )
136 {
137 expressionContext << QgsExpressionContextUtils::parentFormScope( attributecontext.parentFormFeature() );
138 }
139
140 expressionContext.setFeature( mFeature );
141
142 HtmlExpression *htmlExpression = new HtmlExpression();
143 htmlExpression->setExpressionContext( expressionContext );
144 auto frame = mWidget->page()->mainFrame();
145 connect( frame, &QWebFrame::javaScriptWindowObjectCleared, frame, [ = ]
146 {
147 frame->addToJavaScriptWindowObject( QStringLiteral( "expression" ), htmlExpression );
148 } );
149
150 mWidget->setHtml( mHtmlCode );
151}
152
153#ifdef WITH_QTWEBKIT
154void QgsHtmlWidgetWrapper::fixHeight()
155{
156 QWebPage *page = mWidget->page();
157 const int docHeight { page->mainFrame()->contentsSize().height() };
158 mWidget->setFixedHeight( docHeight );
159}
160#endif
161
163{
164 if ( !mWidget )
165 return;
166
167 mFeature = feature;
168 mFormFeature = feature;
169 setHtmlContext();
170}
171
173{
174 return mNeedsGeometry;
175}
176
177
179void HtmlExpression::setExpressionContext( const QgsExpressionContext &context )
180{
181 mExpressionContext = context;
182}
183
184QString HtmlExpression::evaluate( const QString &expression ) const
185{
186 QgsExpression exp { expression };
187 exp.prepare( &mExpressionContext );
188 return exp.evaluate( &mExpressionContext ).toString();
189}
190
191void NeedsGeometryEvaluator::evaluate( const QString &expression )
192{
193 QgsExpression exp { expression };
194 exp.prepare( &mExpressionContext );
195 mNeedsGeometry |= exp.needsGeometry();
196}
197
198void NeedsGeometryEvaluator::setExpressionContext( const QgsExpressionContext &context )
199{
200 mExpressionContext = context;
201}
202
203
The QWebPage class is a collection of stubs to mimic the API of a QWebPage on systems where QtWebkit ...
Definition: qgswebpage.h:104
This class 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.
void widgetValueChanged(const QString &attribute, const QVariant &value, bool attributeChanged)
Notifies about changes of attributes.
const QgsFeature & feature()
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.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
Definition: qgsfeature.cpp:262
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:216
void reinitWidget()
Clears the content and makes new initialization.
void initWidget(QWidget *editor) override
This method should initialize the editor widget with runtime data.
QWidget * createWidget(QWidget *parent) override
This method should create a new widget with the provided parent.
QgsHtmlWidgetWrapper(QgsVectorLayer *layer, QWidget *editor, QWidget *parent)
Create a html widget wrapper.
void setHtmlCode(const QString &htmlCode)
Sets the HTML code to htmlCode.
bool valid() const override
Returns true if the widget has been properly initialized.
void setFeature(const QgsFeature &feature) override
bool needsGeometry() const
Returns true if the widget needs feature geometry.
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 data sets.
QgsExpressionContext createExpressionContext() const FINAL
This method needs to be reimplemented in all classes which implement this interface and return an exp...
The QgsWebView class is a collection of stubs to mimic the API of QWebView on systems where the real ...
Definition: qgswebview.h:66
Manages an editor widget Widget and wrapper share the same parent.
void contextChanged()
Signal when QgsAttributeEditorContext mContext changed.
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.