QGIS API Documentation 3.41.0-Master (fda2aa46e9a)
Loading...
Searching...
No Matches
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
18#include "moc_qgshtmlwidgetwrapper.cpp"
20#include "qgswebframe.h"
22#include "qgsattributeform.h"
23
24#include <QScreen>
25
26QgsHtmlWidgetWrapper::QgsHtmlWidgetWrapper( QgsVectorLayer *layer, QWidget *editor, QWidget *parent )
27 : QgsWidgetWrapper( layer, editor, parent )
28{
29 connect( this, &QgsWidgetWrapper::contextChanged, this, &QgsHtmlWidgetWrapper::setHtmlContext );
30}
31
33{
34 return true;
35}
36
37QWidget *QgsHtmlWidgetWrapper::createWidget( QWidget *parent )
38{
39 QgsAttributeForm *form = qobject_cast<QgsAttributeForm *>( parent );
40
41 if ( form )
42 {
43 mFormFeature = form->feature();
44 connect( form, &QgsAttributeForm::widgetValueChanged, this, [ = ]( const QString & attribute, const QVariant & newValue, bool attributeChanged )
45 {
46 if ( attributeChanged )
47 {
48 if ( mRequiresFormScope )
49 {
50 mFormFeature.setAttribute( attribute, newValue );
51 setHtmlContext();
52 }
53 }
54 } );
55 }
56
57 return new QgsWebView( parent );
58}
59
60void QgsHtmlWidgetWrapper::initWidget( QWidget *editor )
61{
62 mWidget = qobject_cast<QgsWebView *>( editor );
63
64 if ( !mWidget )
65 return;
66
67 mWidget->setHtml( mHtmlCode.replace( "\n", " " ) );
68
69#ifdef WITH_QTWEBKIT
70
71 const int horizontalDpi = mWidget->logicalDpiX();
72
73 mWidget->setZoomFactor( horizontalDpi / 96.0 );
74
75 QWebPage *page = mWidget->page();
76 connect( page, &QWebPage::contentsChanged, this, &QgsHtmlWidgetWrapper::fixHeight, Qt::ConnectionType::UniqueConnection );
77 connect( page, &QWebPage::loadFinished, this, &QgsHtmlWidgetWrapper::fixHeight, Qt::ConnectionType::UniqueConnection );
78
79#endif
80
81 checkGeometryNeeds();
82}
83
85{
86 if ( !mWidget )
87 return;
88
89 initWidget( mWidget );
90}
91
92void QgsHtmlWidgetWrapper::checkGeometryNeeds()
93{
94 if ( !mWidget )
95 return;
96
97 // initialize a temporary QgsWebView to render HTML code and check if one evaluated expression
98 // needs geometry
99 QgsWebView webView;
100 NeedsGeometryEvaluator evaluator;
101
102 const QgsAttributeEditorContext attributecontext = context();
103 if ( QgsVectorLayer *vl = layer() )
104 {
105 const QgsExpressionContext expressionContext = vl->createExpressionContext();
106 evaluator.setExpressionContext( expressionContext );
107 }
108
109 auto frame = webView.page()->mainFrame();
110 connect( frame, &QWebFrame::javaScriptWindowObjectCleared, frame, [ frame, &evaluator ]
111 {
112 frame->addToJavaScriptWindowObject( QStringLiteral( "expression" ), &evaluator );
113 } );
114
115 webView.setHtml( mHtmlCode );
116
117 mNeedsGeometry = evaluator.needsGeometry();
118}
119
120void QgsHtmlWidgetWrapper::setHtmlCode( const QString &htmlCode )
121{
122 mHtmlCode = htmlCode;
123
124 bool ok = false;
125 const thread_local QRegularExpression expRe( QStringLiteral( R"re(expression.evaluate\s*\‍(\s*"(.*)"\))re" ), QRegularExpression::PatternOption::MultilineOption | QRegularExpression::PatternOption::DotMatchesEverythingOption );
126 QRegularExpressionMatchIterator matchIt = expRe.globalMatch( mHtmlCode );
127 while ( !ok && matchIt.hasNext() )
128 {
129 const QRegularExpressionMatch match = matchIt.next();
130 const QgsExpression exp = match.captured( 1 );
132 }
133 mRequiresFormScope = ok;
134
135 checkGeometryNeeds();
136}
137
138void QgsHtmlWidgetWrapper::setHtmlContext( )
139{
140 if ( !mWidget )
141 return;
142
143 const QgsAttributeEditorContext attributecontext = context();
144 QgsExpressionContext expressionContext = layer()->createExpressionContext();
145 expressionContext << QgsExpressionContextUtils::formScope( mFormFeature, attributecontext.attributeFormModeString() );
146 if ( attributecontext.parentFormFeature().isValid() )
147 {
148 expressionContext << QgsExpressionContextUtils::parentFormScope( attributecontext.parentFormFeature() );
149 }
150
151 expressionContext.setFeature( mFeature );
152
153 HtmlExpression *htmlExpression = new HtmlExpression();
154 htmlExpression->setExpressionContext( expressionContext );
155 auto frame = mWidget->page()->mainFrame();
156 connect( frame, &QWebFrame::javaScriptWindowObjectCleared, frame, [ = ]
157 {
158 frame->addToJavaScriptWindowObject( QStringLiteral( "expression" ), htmlExpression );
159 } );
160
161 mWidget->setHtml( mHtmlCode );
162}
163
164#ifdef WITH_QTWEBKIT
165void QgsHtmlWidgetWrapper::fixHeight()
166{
167 QWebPage *page = mWidget->page();
168 const int docHeight { page->mainFrame()->contentsSize().height() };
169 mWidget->setFixedHeight( docHeight );
170}
171#endif
172
174{
175 if ( !mWidget )
176 return;
177
178 mFeature = feature;
179 mFormFeature = feature;
180 setHtmlContext();
181}
182
184{
185 return mNeedsGeometry;
186}
187
188
190void HtmlExpression::setExpressionContext( const QgsExpressionContext &context )
191{
192 mExpressionContext = context;
193}
194
195QString HtmlExpression::evaluate( const QString &expression ) const
196{
197 QgsExpression exp { expression };
198 exp.prepare( &mExpressionContext );
199 return exp.evaluate( &mExpressionContext ).toString();
200}
201
202void NeedsGeometryEvaluator::evaluate( const QString &expression )
203{
204 QgsExpression exp { expression };
205 exp.prepare( &mExpressionContext );
206 mNeedsGeometry |= exp.needsGeometry();
207}
208
209void NeedsGeometryEvaluator::setExpressionContext( const QgsExpressionContext &context )
210{
211 mExpressionContext = context;
212}
213
214
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:58
Q_INVOKABLE bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
bool isValid() const
Returns the validity of this feature.
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.