QGIS API Documentation 4.1.0-Master (60fea48833c)
Loading...
Searching...
No Matches
qgslayoutitemhtml.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitemhtml.cpp
3 ------------------------------------------------------------
4 begin : October 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgslayoutitemhtml.h"
17
18#include "qgsdistancearea.h"
19#include "qgsexpression.h"
20#include "qgsjsonutils.h"
21#include "qgslayout.h"
22#include "qgslayoutframe.h"
23#include "qgslayoutitemlabel.h"
24#include "qgslayoutitemmap.h"
27#include "qgslogger.h"
28#include "qgsmapsettings.h"
29#include "qgsmessagelog.h"
32#include "qgsproject.h"
33#include "qgsvectorlayer.h"
34#include "qgswebframe.h"
35#include "qgswebpage.h"
36
37#include <QAbstractTextDocumentLayout>
38#include <QCoreApplication>
39#include <QImage>
40#include <QNetworkReply>
41#include <QPainter>
42#include <QString>
43#include <QThread>
44#include <QUrl>
45
46#include "moc_qgslayoutitemhtml.cpp"
47
48using namespace Qt::StringLiterals;
49
50// clazy:excludeall=lambda-in-connect
51
54{
55 mHtmlUnitsToLayoutUnits = htmlUnitsToLayoutUnits();
56
57 // only possible on the main thread!
58 if ( QThread::currentThread() == QApplication::instance()->thread() )
59 {
60 mWebPage = std::make_unique< QgsWebPage >();
61 }
62 else
63 {
64 QgsMessageLog::logMessage( QObject::tr( "Cannot load HTML content in background threads" ) );
65 }
66
67 if ( mWebPage )
68 {
69 mWebPage->setIdentifier( tr( "Layout HTML item" ) );
70 mWebPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
71 mWebPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
72
73 //This makes the background transparent. Found on http://blog.qt.digia.com/blog/2009/06/30/transparent-qwebview-or-qwebpage/
74 QPalette palette = mWebPage->palette();
75 palette.setBrush( QPalette::Base, Qt::transparent );
76 mWebPage->setPalette( palette );
77
78 mWebPage->setNetworkAccessManager( QgsNetworkAccessManager::instance() );
79 }
80
81 //a html item added to a layout needs to have the initial expression context set,
82 //otherwise fields in the html aren't correctly evaluated until atlas preview feature changes (#9457)
83 setExpressionContext( mLayout->reportContext().feature(), mLayout->reportContext().layer() );
84
85 connect( &mLayout->reportContext(), &QgsLayoutReportContext::changed, this, &QgsLayoutItemHtml::refreshExpressionContext );
86
87 mFetcher = new QgsNetworkContentFetcher();
88}
89
91{
92 mFetcher->deleteLater();
93}
94
99
101{
102 return QgsApplication::getThemeIcon( u"/mLayoutItemHtml.svg"_s );
103}
104
109
111{
113 QgsLayoutFrame *frame = new QgsLayoutFrame( label->layout(), html );
114 frame->setVisible( label->isVisible() );
115 frame->setLocked( label->isLocked() );
116 frame->setItemOpacity( label->itemOpacity() );
117 frame->setRotation( label->rotation() );
118 frame->setReferencePoint( label->referencePoint() );
119 frame->attemptMove( label->positionWithUnits() );
120 frame->attemptResize( label->sizeWithUnits() );
121 frame->setZValue( label->zValue() );
122 frame->setParentGroup( label->parentGroup() );
123 frame->setBackgroundColor( label->backgroundColor() );
124 frame->setFrameEnabled( label->frameEnabled() );
125 frame->setFrameJoinStyle( label->frameJoinStyle() );
126 frame->setFrameStrokeWidth( label->frameStrokeWidth() );
127 frame->setFrameStrokeColor( label->frameStrokeColor() );
128 html->addFrame( frame );
129 html->setContentMode( QgsLayoutItemHtml::ManualHtml );
130 html->setHtml( label->currentText() );
131 html->setUserStylesheetEnabled( true );
132 html->setUserStylesheet( label->createStylesheet() );
133 html->loadHtml();
134 return html;
135}
136
138{
139 if ( !mWebPage )
140 {
141 return;
142 }
143
144 mUrl = url;
145 loadHtml( true );
146 emit changed();
147}
148
149void QgsLayoutItemHtml::setHtml( const QString &html )
150{
151 mHtml = html;
152 //TODO - this signal should be emitted, but without changing the signal which sets the html
153 //to an equivalent of editingFinished it causes a lot of problems. Need to investigate
154 //ways of doing this using QScintilla widgets.
155 //emit changed();
156}
157
159{
160 mEvaluateExpressions = evaluateExpressions;
161 loadHtml( true );
162 emit changed();
163}
164
165void QgsLayoutItemHtml::loadHtml( const bool useCache, const QgsExpressionContext *context )
166{
167 if ( !mWebPage )
168 {
169 return;
170 }
171
172 const QgsExpressionContext scopedContext = createExpressionContext();
173 const QgsExpressionContext *evalContext = context ? context : &scopedContext;
174
175 QString loadedHtml;
176 switch ( mContentMode )
177 {
179 {
180 QString currentUrl = mUrl.toString();
181
182 //data defined url set?
183 bool ok = false;
184 currentUrl = mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::SourceUrl, *evalContext, currentUrl, &ok );
185 if ( ok )
186 {
187 currentUrl = currentUrl.trimmed();
188 QgsDebugMsgLevel( u"exprVal Source Url:%1"_s.arg( currentUrl ), 2 );
189 }
190 if ( currentUrl.isEmpty() )
191 {
192 return;
193 }
194 if ( !( useCache && currentUrl == mLastFetchedUrl ) )
195 {
196 loadedHtml = fetchHtml( QUrl( currentUrl ) );
197 mLastFetchedUrl = currentUrl;
198 }
199 else
200 {
201 loadedHtml = mFetchedHtml;
202 }
203
204 break;
205 }
207 loadedHtml = mHtml;
208 break;
209 }
210
211 //evaluate expressions
212 if ( mEvaluateExpressions )
213 {
214 loadedHtml = QgsExpression::replaceExpressionText( loadedHtml, evalContext, &mDistanceArea );
215 }
216
217 bool loaded = false;
218
219 QEventLoop loop;
220 connect( mWebPage.get(), &QWebPage::loadFinished, &loop, [&loaded, &loop] {
221 loaded = true;
222 loop.quit();
223 } );
224 connect( mFetcher, &QgsNetworkContentFetcher::finished, &loop, [&loaded, &loop] {
225 loaded = true;
226 loop.quit();
227 } );
228
229 //reset page size. otherwise viewport size increases but never decreases again
230 mWebPage->setViewportSize( QSize( maxFrameWidth() * mHtmlUnitsToLayoutUnits, 0 ) );
231
232 //set html, using the specified url as base if in Url mode or the project file if in manual mode
233 const QUrl baseUrl = mContentMode == QgsLayoutItemHtml::Url ? QUrl( mActualFetchedUrl ) : QUrl::fromLocalFile( mLayout->project()->absoluteFilePath() );
234
235 mWebPage->mainFrame()->setHtml( loadedHtml, baseUrl );
236
237 //set user stylesheet
238 QWebSettings *settings = mWebPage->settings();
239 if ( mEnableUserStylesheet && !mUserStylesheet.isEmpty() )
240 {
241 QByteArray ba;
242 ba.append( mUserStylesheet.toUtf8() );
243 const QUrl cssFileURL = QUrl( QString( "data:text/css;charset=utf-8;base64," + ba.toBase64() ) );
244 settings->setUserStyleSheetUrl( cssFileURL );
245 }
246 else
247 {
248 settings->setUserStyleSheetUrl( QUrl() );
249 }
250
251 if ( !loaded )
252 loop.exec( QEventLoop::ExcludeUserInputEvents );
253
255 //trigger a repaint
256 emit contentsChanged();
257}
258
259double QgsLayoutItemHtml::maxFrameWidth() const
260{
261 double maxWidth = 0;
263 {
264 maxWidth = std::max( maxWidth, static_cast< double >( frame->boundingRect().width() ) );
265 }
266
267 return maxWidth;
268}
269
271{
272 if ( frameCount() < 1 )
273 return;
274
275 if ( !mWebPage )
276 return;
277
278 QSize contentsSize = mWebPage->mainFrame()->contentsSize();
279
280 //find maximum frame width
281 const double maxWidth = maxFrameWidth();
282 //set content width to match maximum frame width
283 contentsSize.setWidth( maxWidth * mHtmlUnitsToLayoutUnits );
284
285 mWebPage->setViewportSize( contentsSize );
286 mSize.setWidth( contentsSize.width() / mHtmlUnitsToLayoutUnits );
287 mSize.setHeight( contentsSize.height() / mHtmlUnitsToLayoutUnits );
288 if ( contentsSize.isValid() )
289 {
290 renderCachedImage();
291 }
293 emit changed();
294}
295
296void QgsLayoutItemHtml::renderCachedImage()
297{
298 if ( !mWebPage )
299 return;
300
301 //render page to cache image
302 mRenderedPage = QImage( mWebPage->viewportSize(), QImage::Format_ARGB32 );
303 if ( mRenderedPage.isNull() )
304 {
305 return;
306 }
307 mRenderedPage.fill( Qt::transparent );
308 QPainter painter;
309 painter.begin( &mRenderedPage );
310 mWebPage->mainFrame()->render( &painter );
311 painter.end();
312}
313
314QString QgsLayoutItemHtml::fetchHtml( const QUrl &url )
315{
316 //pause until HTML fetch
317 bool loaded = false;
318 QEventLoop loop;
319 connect( mFetcher, &QgsNetworkContentFetcher::finished, &loop, [&loaded, &loop] {
320 loaded = true;
321 loop.quit();
322 } );
323 mFetcher->fetchContent( url );
324
325 if ( !loaded )
326 loop.exec( QEventLoop::ExcludeUserInputEvents );
327
328 mFetchedHtml = mFetcher->contentAsString();
329 mActualFetchedUrl = mFetcher->reply()->url().toString();
330 return mFetchedHtml;
331}
332
334{
335 return mSize;
336}
337
338void QgsLayoutItemHtml::render( QgsLayoutItemRenderContext &context, const QRectF &renderExtent, const int frameIndex )
339{
340 Q_UNUSED( renderExtent )
341 if ( mLayout->renderContext().isPreviewRender() )
342 {
343 if ( QgsLayoutFrame *currentFrame = frame( frameIndex ) )
344 {
345 QPainter *painter = context.renderContext().painter();
346
347 // painter is scaled to dots, so scale back to layout units
348 const QRectF painterRect = QRectF(
349 currentFrame->rect().left() * context.renderContext().scaleFactor(),
350 currentFrame->rect().top() * context.renderContext().scaleFactor(),
351 currentFrame->rect().width() * context.renderContext().scaleFactor(),
352 currentFrame->rect().height() * context.renderContext().scaleFactor()
353 );
354
355 painter->setBrush( QBrush( QColor( 255, 125, 125, 125 ) ) );
356 painter->setPen( Qt::NoPen );
357 painter->drawRect( painterRect );
358 painter->setBrush( Qt::NoBrush );
359
360 painter->setPen( QColor( 200, 0, 0, 255 ) );
361 QTextDocument td;
362 td.setTextWidth( painterRect.width() );
363 td.setHtml(
364 u"<span style=\"color: rgb(200,0,0);\"><b>%1</b><br>%2</span>"_s.arg( tr( "WebKit not available!" ), tr( "The item cannot be rendered because this QGIS install was built without WebKit support." ) )
365 );
366 painter->setClipRect( painterRect );
367 QAbstractTextDocumentLayout::PaintContext ctx;
368 td.documentLayout()->draw( painter, ctx );
369 }
370 }
371}
372
373double QgsLayoutItemHtml::htmlUnitsToLayoutUnits()
374{
375 if ( !mLayout )
376 {
377 return 1.0;
378 }
379
380 return mLayout->convertToLayoutUnits( QgsLayoutMeasurement( mLayout->renderContext().dpi() / 72.0, Qgis::LayoutUnit::Millimeters ) ); //webkit seems to assume a standard dpi of 96
381}
382
383bool candidateSort( QPair<int, int> c1, QPair<int, int> c2 )
384{
385 if ( c1.second < c2.second )
386 return true;
387 else if ( c1.second > c2.second )
388 return false;
389 else if ( c1.first > c2.first )
390 return true;
391 else
392 return false;
393}
394
396{
397 if ( !mWebPage || mRenderedPage.isNull() || !mUseSmartBreaks )
398 {
399 return yPos;
400 }
401
402 //convert yPos to pixels
403 const int idealPos = yPos * htmlUnitsToLayoutUnits();
404
405 //if ideal break pos is past end of page, there's nothing we need to do
406 if ( idealPos >= mRenderedPage.height() )
407 {
408 return yPos;
409 }
410
411 const int maxSearchDistance = mMaxBreakDistance * htmlUnitsToLayoutUnits();
412
413 //loop through all lines just before ideal break location, up to max distance
414 //of maxSearchDistance
415 int changes = 0;
416 QRgb currentColor;
417 bool currentPixelTransparent = false;
418 bool previousPixelTransparent = false;
419 QRgb pixelColor;
420 QList< QPair<int, int> > candidates;
421 const int minRow = std::max( idealPos - maxSearchDistance, 0 );
422 for ( int candidateRow = idealPos; candidateRow >= minRow; --candidateRow )
423 {
424 changes = 0;
425 currentColor = qRgba( 0, 0, 0, 0 );
426 //check all pixels in this line
427 for ( int col = 0; col < mRenderedPage.width(); ++col )
428 {
429 //count how many times the pixels change color in this row
430 //eventually, we select a row to break at with the minimum number of color changes
431 //since this is likely a line break, or gap between table cells, etc
432 //but very unlikely to be midway through a text line or picture
433 pixelColor = mRenderedPage.pixel( col, candidateRow );
434 currentPixelTransparent = qAlpha( pixelColor ) == 0;
435 if ( pixelColor != currentColor && !( currentPixelTransparent && previousPixelTransparent ) )
436 {
437 //color has changed
438 currentColor = pixelColor;
439 changes++;
440 }
441 previousPixelTransparent = currentPixelTransparent;
442 }
443 candidates.append( qMakePair( candidateRow, changes ) );
444 }
445
446 //sort candidate rows by number of changes ascending, row number descending
447 std::sort( candidates.begin(), candidates.end(), candidateSort );
448 //first candidate is now the largest row with smallest number of changes
449
450 //OK, now take the mid point of the best candidate position
451 //we do this so that the spacing between text lines is likely to be split in half
452 //otherwise the html will be broken immediately above a line of text, which
453 //looks a little messy
454 const int maxCandidateRow = candidates[0].first;
455 int minCandidateRow = maxCandidateRow + 1;
456 const int minCandidateChanges = candidates[0].second;
457
458 QList< QPair<int, int> >::iterator it;
459 for ( it = candidates.begin(); it != candidates.end(); ++it )
460 {
461 if ( ( *it ).second != minCandidateChanges || ( *it ).first != minCandidateRow - 1 )
462 {
463 //no longer in a consecutive block of rows of minimum pixel color changes
464 //so return the row mid-way through the block
465 //first converting back to mm
466 return ( minCandidateRow + ( maxCandidateRow - minCandidateRow ) / 2 ) / htmlUnitsToLayoutUnits();
467 }
468 minCandidateRow = ( *it ).first;
469 }
470
471 //above loop didn't work for some reason
472 //return first candidate converted to mm
473 return candidates[0].first / htmlUnitsToLayoutUnits();
474}
475
477{
478 mUseSmartBreaks = useSmartBreaks;
480 emit changed();
481}
482
484{
485 mMaxBreakDistance = maxBreakDistance;
487 emit changed();
488}
489
490void QgsLayoutItemHtml::setUserStylesheet( const QString &stylesheet )
491{
492 mUserStylesheet = stylesheet;
493 //TODO - this signal should be emitted, but without changing the signal which sets the css
494 //to an equivalent of editingFinished it causes a lot of problems. Need to investigate
495 //ways of doing this using QScintilla widgets.
496 //emit changed();
497}
498
499void QgsLayoutItemHtml::setUserStylesheetEnabled( const bool stylesheetEnabled )
500{
501 if ( mEnableUserStylesheet != stylesheetEnabled )
502 {
503 mEnableUserStylesheet = stylesheetEnabled;
504 loadHtml( true );
505 emit changed();
506 }
507}
508
510{
511 return tr( "<HTML frame>" );
512}
513
514bool QgsLayoutItemHtml::writePropertiesToElement( QDomElement &htmlElem, QDomDocument &, const QgsReadWriteContext & ) const
515{
516 htmlElem.setAttribute( u"contentMode"_s, QString::number( static_cast< int >( mContentMode ) ) );
517 htmlElem.setAttribute( u"url"_s, mUrl.toString() );
518 htmlElem.setAttribute( u"html"_s, mHtml );
519 htmlElem.setAttribute( u"evaluateExpressions"_s, mEvaluateExpressions ? "true" : "false" );
520 htmlElem.setAttribute( u"useSmartBreaks"_s, mUseSmartBreaks ? "true" : "false" );
521 htmlElem.setAttribute( u"maxBreakDistance"_s, QString::number( mMaxBreakDistance ) );
522 htmlElem.setAttribute( u"stylesheet"_s, mUserStylesheet );
523 htmlElem.setAttribute( u"stylesheetEnabled"_s, mEnableUserStylesheet ? "true" : "false" );
524 return true;
525}
526
527bool QgsLayoutItemHtml::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &, const QgsReadWriteContext & )
528{
529 bool contentModeOK;
530 mContentMode = static_cast< QgsLayoutItemHtml::ContentMode >( itemElem.attribute( u"contentMode"_s ).toInt( &contentModeOK ) );
531 if ( !contentModeOK )
532 {
533 mContentMode = QgsLayoutItemHtml::Url;
534 }
535 mEvaluateExpressions = itemElem.attribute( u"evaluateExpressions"_s, u"true"_s ) == "true"_L1;
536 mUseSmartBreaks = itemElem.attribute( u"useSmartBreaks"_s, u"true"_s ) == "true"_L1;
537 mMaxBreakDistance = itemElem.attribute( u"maxBreakDistance"_s, u"10"_s ).toDouble();
538 mHtml = itemElem.attribute( u"html"_s );
539 mUserStylesheet = itemElem.attribute( u"stylesheet"_s );
540 mEnableUserStylesheet = itemElem.attribute( u"stylesheetEnabled"_s, u"false"_s ) == "true"_L1;
541
542 //finally load the set url
543 const QString urlString = itemElem.attribute( u"url"_s );
544 if ( !urlString.isEmpty() )
545 {
546 mUrl = urlString;
547 }
548 loadHtml( true );
549
550 //since frames had to be created before, we need to emit a changed signal to refresh the widget
551 emit changed();
552 return true;
553}
554
555void QgsLayoutItemHtml::setExpressionContext( const QgsFeature &feature, QgsVectorLayer *layer )
556{
557 mExpressionFeature = feature;
558 mExpressionLayer = layer;
559
560 //setup distance area conversion
561 if ( layer )
562 {
563 mDistanceArea.setSourceCrs( layer->crs(), mLayout->project()->transformContext() );
564 }
565 else if ( mLayout )
566 {
567 //set to composition's mapsettings' crs
568 QgsLayoutItemMap *referenceMap = mLayout->referenceMap();
569 if ( referenceMap )
570 mDistanceArea.setSourceCrs( referenceMap->crs(), mLayout->project()->transformContext() );
571 }
572 if ( mLayout )
573 {
574 mDistanceArea.setEllipsoid( mLayout->project()->ellipsoid() );
575 }
576
577 if ( feature.isValid() )
578 {
579 // create JSON representation of feature
580 QgsJsonExporter exporter( layer );
581 exporter.setIncludeRelated( true );
582 mAtlasFeatureJSON = exporter.exportFeature( feature );
583 }
584 else
585 {
586 mAtlasFeatureJSON.clear();
587 }
588}
589
590void QgsLayoutItemHtml::refreshExpressionContext()
591{
592 QgsVectorLayer *vl = nullptr;
593 QgsFeature feature;
594
595 if ( mLayout )
596 {
597 vl = mLayout->reportContext().layer();
598 feature = mLayout->reportContext().feature();
599 }
600
601 setExpressionContext( feature, vl );
602 loadHtml( true );
603}
604
606{
608
609 //updates data defined properties and redraws item to match
611 {
612 loadHtml( true, &context );
613 }
614}
615
616//JavascriptExecutorLoop
618
619void JavascriptExecutorLoop::done()
620{
621 mDone = true;
622 quit();
623}
624
625void JavascriptExecutorLoop::execIfNotDone()
626{
627 if ( !mDone )
628 exec( QEventLoop::ExcludeUserInputEvents );
629
630 // gross, but nothing else works, so f*** it.. it's not worth spending a day trying to find a non-hacky way
631 // to force the web page to update following the js execution
632 for ( int i = 0; i < 100; i++ )
633 qApp->processEvents();
634}
635
636void JavascriptExecutorLoop::reportError( const QString &error )
637{
638 mDone = true;
639 QgsMessageLog::logMessage( tr( "HTML setFeature function error: %1" ).arg( error ), tr( "Layout" ) );
640 quit();
641}
642
A collection of stubs to mimic the API of a QWebSettings on systems where QtWebkit is not available.
Definition qgswebpage.h:38
@ Millimeters
Millimeters.
Definition qgis.h:5361
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
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.
Base class for frame items, which form a layout multiframe item.
int type() const override
Returns unique multiframe type id.
QSizeF totalSize() const override
Returns the total size of the multiframe's content, in layout units.
void setUrl(const QUrl &url)
Sets the url for content to display in the item when the item is using the QgsLayoutItemHtml::Url mod...
ContentMode
Source modes for the HTML content to render in the item.
@ ManualHtml
HTML content is manually set for the item.
@ Url
Using this mode item fetches its content via a url.
void setEvaluateExpressions(bool evaluateExpressions)
Sets whether the html item will evaluate QGIS expressions prior to rendering the HTML content.
bool readPropertiesFromElement(const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context) override
Sets multiframe state from a DOM element.
QString html() const
Returns the HTML source displayed in the item if the item is using the QgsLayoutItemHtml::ManualHtml ...
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::DataDefinedProperty::AllProperties) override
double maxBreakDistance() const
Returns the maximum distance allowed when calculating where to place page breaks in the html.
static QgsLayoutItemHtml * createFromLabel(QgsLayoutItemLabel *label)
Returns a new QgsLayoutItemHtml matching the content and rendering of a given label.
static QgsLayoutItemHtml * create(QgsLayout *layout)
Returns a new QgsLayoutItemHtml for the specified parent layout.
bool evaluateExpressions() const
Returns whether html item will evaluate QGIS expressions prior to rendering the HTML content.
double findNearbyPageBreak(double yPos) override
Finds the optimal position to break a frame at.
QUrl url() const
Returns the URL of the content displayed in the item if the item is using the QgsLayoutItemHtml::Url ...
void setMaxBreakDistance(double distance)
Sets the maximum distance allowed when calculating where to place page breaks in the html.
void setUserStylesheetEnabled(bool enabled)
Sets whether user stylesheets are enabled for the HTML content.
void setHtml(const QString &html)
Sets the html to display in the item when the item is using the QgsLayoutItemHtml::ManualHtml mode.
QString displayName() const override
Returns the multiframe display name.
void setUseSmartBreaks(bool useSmartBreaks)
Sets whether the html item should use smart breaks.
void recalculateFrameSizes() override
Recalculates the frame sizes for the current viewport dimensions.
void setUserStylesheet(const QString &stylesheet)
Sets the user stylesheet CSS rules to use while rendering the HTML content.
QIcon icon() const override
Returns the item's icon.
bool writePropertiesToElement(QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Stores multiframe state within an XML DOM element.
QgsLayoutItemHtml(QgsLayout *layout)
Constructor for QgsLayoutItemHtml, with the specified parent layout.
void loadHtml(bool useCache=false, const QgsExpressionContext *context=nullptr)
Reloads the html source from the url and redraws the item.
void render(QgsLayoutItemRenderContext &context, const QRectF &renderExtent, int frameIndex) override
Renders a portion of the multiframe's content into a render context.
bool useSmartBreaks() const
Returns whether html item is using smart breaks.
A layout item subclass for text labels.
QString currentText() const
Returns the text as it appears on the label (with evaluated expressions and other dynamic content).
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
@ LayoutHtml
Html multiframe item.
Contains settings and helpers relating to a render of a QgsLayoutItem.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
QColor backgroundColor(bool useDataDefined=true) const
Returns the background color for this item.
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
QgsLayoutItemGroup * parentGroup() const
Returns the item's parent group, if the item is part of a QgsLayoutItemGroup group.
QgsLayoutMeasurement frameStrokeWidth() const
Returns the frame's stroke width.
bool isLocked() const
Returns true if the item is locked, and cannot be interacted with using the mouse.
double itemOpacity() const
Returns the item's opacity.
ReferencePoint referencePoint() const
Returns the reference point for positioning of the layout item.
QgsLayoutPoint positionWithUnits() const
Returns the item's current position, including units.
bool frameEnabled() const
Returns true if the item includes a frame.
QColor frameStrokeColor() const
Returns the frame's stroke color.
Qt::PenJoinStyle frameJoinStyle() const
Returns the join style used for drawing the item's frame.
int frameCount() const
Returns the number of frames associated with this multiframe.
QgsLayoutMultiFrame(QgsLayout *layout)
Construct a new multiframe item, attached to the specified layout.
void contentsChanged()
Emitted when the contents of the multi frame have changed and the frames must be redrawn.
QgsLayoutFrame * frame(int index) const
Returns the child frame at a specified index from the multiframe.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QList< QgsLayoutFrame * > mFrameItems
virtual void recalculateFrameSizes()
Recalculates the portion of the multiframe item which is shown in each of its component frames.
int frameIndex(QgsLayoutFrame *frame) const
Returns the index of a frame within the multiframe.
QgsPropertyCollection mDataDefinedProperties
const QgsLayout * layout() const
Returns the layout the object is attached to.
void changed()
Emitted when the object's properties change.
QPointer< QgsLayout > mLayout
DataDefinedProperty
Data defined properties for different item types.
@ AllProperties
All properties for item.
void changed()
Emitted certain settings in the context is changed, e.g.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
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).
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
HTTP network content fetcher.
void finished()
Emitted when content has loaded.
A container for the context for various read/write operations on objects.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
QPainter * painter()
Returns the destination QPainter for the render operation.
Represents a vector layer which manages a vector based dataset.
bool candidateSort(QPair< int, int > c1, QPair< int, int > c2)
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63