QGIS API Documentation 3.99.0-Master (a8882ad4560)
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 <QThread>
43#include <QUrl>
44
45#include "moc_qgslayoutitemhtml.cpp"
46
47// clazy:excludeall=lambda-in-connect
48
51{
52 mHtmlUnitsToLayoutUnits = htmlUnitsToLayoutUnits();
53
54 // only possible on the main thread!
55 if ( QThread::currentThread() == QApplication::instance()->thread() )
56 {
57 mWebPage = std::make_unique< QgsWebPage >();
58 }
59 else
60 {
61 QgsMessageLog::logMessage( QObject::tr( "Cannot load HTML content in background threads" ) );
62 }
63
64 if ( mWebPage )
65 {
66 mWebPage->setIdentifier( tr( "Layout HTML item" ) );
67 mWebPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
68 mWebPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
69
70 //This makes the background transparent. Found on http://blog.qt.digia.com/blog/2009/06/30/transparent-qwebview-or-qwebpage/
71 QPalette palette = mWebPage->palette();
72 palette.setBrush( QPalette::Base, Qt::transparent );
73 mWebPage->setPalette( palette );
74
75 mWebPage->setNetworkAccessManager( QgsNetworkAccessManager::instance() );
76 }
77
78 //a html item added to a layout needs to have the initial expression context set,
79 //otherwise fields in the html aren't correctly evaluated until atlas preview feature changes (#9457)
80 setExpressionContext( mLayout->reportContext().feature(), mLayout->reportContext().layer() );
81
82 connect( &mLayout->reportContext(), &QgsLayoutReportContext::changed, this, &QgsLayoutItemHtml::refreshExpressionContext );
83
84 mFetcher = new QgsNetworkContentFetcher();
85}
86
88{
89 mFetcher->deleteLater();
90}
91
96
98{
99 return QgsApplication::getThemeIcon( u"/mLayoutItemHtml.svg"_s );
100}
101
106
108{
110 QgsLayoutFrame *frame = new QgsLayoutFrame( label->layout(), html );
111 frame->setVisible( label->isVisible() );
112 frame->setLocked( label->isLocked() );
113 frame->setItemOpacity( label->itemOpacity() );
114 frame->setRotation( label->rotation() );
115 frame->setReferencePoint( label->referencePoint() );
116 frame->attemptMove( label->positionWithUnits() );
117 frame->attemptResize( label->sizeWithUnits() );
118 frame->setZValue( label->zValue() );
119 frame->setParentGroup( label->parentGroup() );
120 frame->setBackgroundColor( label->backgroundColor() );
121 frame->setFrameEnabled( label->frameEnabled() );
122 frame->setFrameJoinStyle( label->frameJoinStyle() );
123 frame->setFrameStrokeWidth( label->frameStrokeWidth() );
124 frame->setFrameStrokeColor( label->frameStrokeColor() );
125 html->addFrame( frame );
126 html->setContentMode( QgsLayoutItemHtml::ManualHtml );
127 html->setHtml( label->currentText() );
128 html->setUserStylesheetEnabled( true );
129 html->setUserStylesheet( label->createStylesheet() );
130 html->loadHtml();
131 return html;
132}
133
135{
136 if ( !mWebPage )
137 {
138 return;
139 }
140
141 mUrl = url;
142 loadHtml( true );
143 emit changed();
144}
145
146void QgsLayoutItemHtml::setHtml( const QString &html )
147{
148 mHtml = html;
149 //TODO - this signal should be emitted, but without changing the signal which sets the html
150 //to an equivalent of editingFinished it causes a lot of problems. Need to investigate
151 //ways of doing this using QScintilla widgets.
152 //emit changed();
153}
154
156{
157 mEvaluateExpressions = evaluateExpressions;
158 loadHtml( true );
159 emit changed();
160}
161
162void QgsLayoutItemHtml::loadHtml( const bool useCache, const QgsExpressionContext *context )
163{
164 if ( !mWebPage )
165 {
166 return;
167 }
168
169 const QgsExpressionContext scopedContext = createExpressionContext();
170 const QgsExpressionContext *evalContext = context ? context : &scopedContext;
171
172 QString loadedHtml;
173 switch ( mContentMode )
174 {
176 {
177
178 QString currentUrl = mUrl.toString();
179
180 //data defined url set?
181 bool ok = false;
182 currentUrl = mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::SourceUrl, *evalContext, currentUrl, &ok );
183 if ( ok )
184 {
185 currentUrl = currentUrl.trimmed();
186 QgsDebugMsgLevel( u"exprVal Source Url:%1"_s.arg( currentUrl ), 2 );
187 }
188 if ( currentUrl.isEmpty() )
189 {
190 return;
191 }
192 if ( !( useCache && currentUrl == mLastFetchedUrl ) )
193 {
194 loadedHtml = fetchHtml( QUrl( currentUrl ) );
195 mLastFetchedUrl = currentUrl;
196 }
197 else
198 {
199 loadedHtml = mFetchedHtml;
200 }
201
202 break;
203 }
205 loadedHtml = mHtml;
206 break;
207 }
208
209 //evaluate expressions
210 if ( mEvaluateExpressions )
211 {
212 loadedHtml = QgsExpression::replaceExpressionText( loadedHtml, evalContext, &mDistanceArea );
213 }
214
215 bool loaded = false;
216
217 QEventLoop loop;
218 connect( mWebPage.get(), &QWebPage::loadFinished, &loop, [&loaded, &loop ] { loaded = true; loop.quit(); } );
219 connect( mFetcher, &QgsNetworkContentFetcher::finished, &loop, [&loaded, &loop ] { loaded = true; loop.quit(); } );
220
221 //reset page size. otherwise viewport size increases but never decreases again
222 mWebPage->setViewportSize( QSize( maxFrameWidth() * mHtmlUnitsToLayoutUnits, 0 ) );
223
224 //set html, using the specified url as base if in Url mode or the project file if in manual mode
225 const QUrl baseUrl = mContentMode == QgsLayoutItemHtml::Url ?
226 QUrl( mActualFetchedUrl ) :
227 QUrl::fromLocalFile( mLayout->project()->absoluteFilePath() );
228
229 mWebPage->mainFrame()->setHtml( loadedHtml, baseUrl );
230
231 //set user stylesheet
232 QWebSettings *settings = mWebPage->settings();
233 if ( mEnableUserStylesheet && ! mUserStylesheet.isEmpty() )
234 {
235 QByteArray ba;
236 ba.append( mUserStylesheet.toUtf8() );
237 const QUrl cssFileURL = QUrl( QString( "data:text/css;charset=utf-8;base64," + ba.toBase64() ) );
238 settings->setUserStyleSheetUrl( cssFileURL );
239 }
240 else
241 {
242 settings->setUserStyleSheetUrl( QUrl() );
243 }
244
245 if ( !loaded )
246 loop.exec( QEventLoop::ExcludeUserInputEvents );
247
249 //trigger a repaint
250 emit contentsChanged();
251}
252
253double QgsLayoutItemHtml::maxFrameWidth() const
254{
255 double maxWidth = 0;
257 {
258 maxWidth = std::max( maxWidth, static_cast< double >( frame->boundingRect().width() ) );
259 }
260
261 return maxWidth;
262}
263
265{
266 if ( frameCount() < 1 )
267 return;
268
269 if ( !mWebPage )
270 return;
271
272 QSize contentsSize = mWebPage->mainFrame()->contentsSize();
273
274 //find maximum frame width
275 const double maxWidth = maxFrameWidth();
276 //set content width to match maximum frame width
277 contentsSize.setWidth( maxWidth * mHtmlUnitsToLayoutUnits );
278
279 mWebPage->setViewportSize( contentsSize );
280 mSize.setWidth( contentsSize.width() / mHtmlUnitsToLayoutUnits );
281 mSize.setHeight( contentsSize.height() / mHtmlUnitsToLayoutUnits );
282 if ( contentsSize.isValid() )
283 {
284 renderCachedImage();
285 }
287 emit changed();
288}
289
290void QgsLayoutItemHtml::renderCachedImage()
291{
292 if ( !mWebPage )
293 return;
294
295 //render page to cache image
296 mRenderedPage = QImage( mWebPage->viewportSize(), QImage::Format_ARGB32 );
297 if ( mRenderedPage.isNull() )
298 {
299 return;
300 }
301 mRenderedPage.fill( Qt::transparent );
302 QPainter painter;
303 painter.begin( &mRenderedPage );
304 mWebPage->mainFrame()->render( &painter );
305 painter.end();
306}
307
308QString QgsLayoutItemHtml::fetchHtml( const QUrl &url )
309{
310 //pause until HTML fetch
311 bool loaded = false;
312 QEventLoop loop;
313 connect( mFetcher, &QgsNetworkContentFetcher::finished, &loop, [&loaded, &loop ] { loaded = true; loop.quit(); } );
314 mFetcher->fetchContent( url );
315
316 if ( !loaded )
317 loop.exec( QEventLoop::ExcludeUserInputEvents );
318
319 mFetchedHtml = mFetcher->contentAsString();
320 mActualFetchedUrl = mFetcher->reply()->url().toString();
321 return mFetchedHtml;
322}
323
325{
326 return mSize;
327}
328
329void QgsLayoutItemHtml::render( QgsLayoutItemRenderContext &context, const QRectF &renderExtent, const int frameIndex )
330{
331 Q_UNUSED( renderExtent )
332 if ( mLayout->renderContext().isPreviewRender() )
333 {
334 if ( QgsLayoutFrame *currentFrame = frame( frameIndex ) )
335 {
336 QPainter *painter = context.renderContext().painter();
337
338 // painter is scaled to dots, so scale back to layout units
339 const QRectF painterRect = QRectF( currentFrame->rect().left() * context.renderContext().scaleFactor(),
340 currentFrame->rect().top() * context.renderContext().scaleFactor(),
341 currentFrame->rect().width() * context.renderContext().scaleFactor(),
342 currentFrame->rect().height() * context.renderContext().scaleFactor()
343 );
344
345 painter->setBrush( QBrush( QColor( 255, 125, 125, 125 ) ) );
346 painter->setPen( Qt::NoPen );
347 painter->drawRect( painterRect );
348 painter->setBrush( Qt::NoBrush );
349
350 painter->setPen( QColor( 200, 0, 0, 255 ) );
351 QTextDocument td;
352 td.setTextWidth( painterRect.width() );
353 td.setHtml( u"<span style=\"color: rgb(200,0,0);\"><b>%1</b><br>%2</span>"_s.arg(
354 tr( "WebKit not available!" ),
355 tr( "The item cannot be rendered because this QGIS install was built without WebKit support." ) ) );
356 painter->setClipRect( painterRect );
357 QAbstractTextDocumentLayout::PaintContext ctx;
358 td.documentLayout()->draw( painter, ctx );
359 }
360 }
361}
362
363double QgsLayoutItemHtml::htmlUnitsToLayoutUnits()
364{
365 if ( !mLayout )
366 {
367 return 1.0;
368 }
369
370 return mLayout->convertToLayoutUnits( QgsLayoutMeasurement( mLayout->renderContext().dpi() / 72.0, Qgis::LayoutUnit::Millimeters ) ); //webkit seems to assume a standard dpi of 96
371}
372
373bool candidateSort( QPair<int, int> c1, QPair<int, int> c2 )
374{
375 if ( c1.second < c2.second )
376 return true;
377 else if ( c1.second > c2.second )
378 return false;
379 else if ( c1.first > c2.first )
380 return true;
381 else
382 return false;
383}
384
386{
387 if ( !mWebPage || mRenderedPage.isNull() || !mUseSmartBreaks )
388 {
389 return yPos;
390 }
391
392 //convert yPos to pixels
393 const int idealPos = yPos * htmlUnitsToLayoutUnits();
394
395 //if ideal break pos is past end of page, there's nothing we need to do
396 if ( idealPos >= mRenderedPage.height() )
397 {
398 return yPos;
399 }
400
401 const int maxSearchDistance = mMaxBreakDistance * htmlUnitsToLayoutUnits();
402
403 //loop through all lines just before ideal break location, up to max distance
404 //of maxSearchDistance
405 int changes = 0;
406 QRgb currentColor;
407 bool currentPixelTransparent = false;
408 bool previousPixelTransparent = false;
409 QRgb pixelColor;
410 QList< QPair<int, int> > candidates;
411 const int minRow = std::max( idealPos - maxSearchDistance, 0 );
412 for ( int candidateRow = idealPos; candidateRow >= minRow; --candidateRow )
413 {
414 changes = 0;
415 currentColor = qRgba( 0, 0, 0, 0 );
416 //check all pixels in this line
417 for ( int col = 0; col < mRenderedPage.width(); ++col )
418 {
419 //count how many times the pixels change color in this row
420 //eventually, we select a row to break at with the minimum number of color changes
421 //since this is likely a line break, or gap between table cells, etc
422 //but very unlikely to be midway through a text line or picture
423 pixelColor = mRenderedPage.pixel( col, candidateRow );
424 currentPixelTransparent = qAlpha( pixelColor ) == 0;
425 if ( pixelColor != currentColor && !( currentPixelTransparent && previousPixelTransparent ) )
426 {
427 //color has changed
428 currentColor = pixelColor;
429 changes++;
430 }
431 previousPixelTransparent = currentPixelTransparent;
432 }
433 candidates.append( qMakePair( candidateRow, changes ) );
434 }
435
436 //sort candidate rows by number of changes ascending, row number descending
437 std::sort( candidates.begin(), candidates.end(), candidateSort );
438 //first candidate is now the largest row with smallest number of changes
439
440 //OK, now take the mid point of the best candidate position
441 //we do this so that the spacing between text lines is likely to be split in half
442 //otherwise the html will be broken immediately above a line of text, which
443 //looks a little messy
444 const int maxCandidateRow = candidates[0].first;
445 int minCandidateRow = maxCandidateRow + 1;
446 const int minCandidateChanges = candidates[0].second;
447
448 QList< QPair<int, int> >::iterator it;
449 for ( it = candidates.begin(); it != candidates.end(); ++it )
450 {
451 if ( ( *it ).second != minCandidateChanges || ( *it ).first != minCandidateRow - 1 )
452 {
453 //no longer in a consecutive block of rows of minimum pixel color changes
454 //so return the row mid-way through the block
455 //first converting back to mm
456 return ( minCandidateRow + ( maxCandidateRow - minCandidateRow ) / 2 ) / htmlUnitsToLayoutUnits();
457 }
458 minCandidateRow = ( *it ).first;
459 }
460
461 //above loop didn't work for some reason
462 //return first candidate converted to mm
463 return candidates[0].first / htmlUnitsToLayoutUnits();
464}
465
467{
468 mUseSmartBreaks = useSmartBreaks;
470 emit changed();
471}
472
474{
475 mMaxBreakDistance = maxBreakDistance;
477 emit changed();
478}
479
480void QgsLayoutItemHtml::setUserStylesheet( const QString &stylesheet )
481{
482 mUserStylesheet = stylesheet;
483 //TODO - this signal should be emitted, but without changing the signal which sets the css
484 //to an equivalent of editingFinished it causes a lot of problems. Need to investigate
485 //ways of doing this using QScintilla widgets.
486 //emit changed();
487}
488
489void QgsLayoutItemHtml::setUserStylesheetEnabled( const bool stylesheetEnabled )
490{
491 if ( mEnableUserStylesheet != stylesheetEnabled )
492 {
493 mEnableUserStylesheet = stylesheetEnabled;
494 loadHtml( true );
495 emit changed();
496 }
497}
498
500{
501 return tr( "<HTML frame>" );
502}
503
504bool QgsLayoutItemHtml::writePropertiesToElement( QDomElement &htmlElem, QDomDocument &, const QgsReadWriteContext & ) const
505{
506 htmlElem.setAttribute( u"contentMode"_s, QString::number( static_cast< int >( mContentMode ) ) );
507 htmlElem.setAttribute( u"url"_s, mUrl.toString() );
508 htmlElem.setAttribute( u"html"_s, mHtml );
509 htmlElem.setAttribute( u"evaluateExpressions"_s, mEvaluateExpressions ? "true" : "false" );
510 htmlElem.setAttribute( u"useSmartBreaks"_s, mUseSmartBreaks ? "true" : "false" );
511 htmlElem.setAttribute( u"maxBreakDistance"_s, QString::number( mMaxBreakDistance ) );
512 htmlElem.setAttribute( u"stylesheet"_s, mUserStylesheet );
513 htmlElem.setAttribute( u"stylesheetEnabled"_s, mEnableUserStylesheet ? "true" : "false" );
514 return true;
515}
516
517bool QgsLayoutItemHtml::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &, const QgsReadWriteContext & )
518{
519 bool contentModeOK;
520 mContentMode = static_cast< QgsLayoutItemHtml::ContentMode >( itemElem.attribute( u"contentMode"_s ).toInt( &contentModeOK ) );
521 if ( !contentModeOK )
522 {
523 mContentMode = QgsLayoutItemHtml::Url;
524 }
525 mEvaluateExpressions = itemElem.attribute( u"evaluateExpressions"_s, u"true"_s ) == "true"_L1;
526 mUseSmartBreaks = itemElem.attribute( u"useSmartBreaks"_s, u"true"_s ) == "true"_L1;
527 mMaxBreakDistance = itemElem.attribute( u"maxBreakDistance"_s, u"10"_s ).toDouble();
528 mHtml = itemElem.attribute( u"html"_s );
529 mUserStylesheet = itemElem.attribute( u"stylesheet"_s );
530 mEnableUserStylesheet = itemElem.attribute( u"stylesheetEnabled"_s, u"false"_s ) == "true"_L1;
531
532 //finally load the set url
533 const QString urlString = itemElem.attribute( u"url"_s );
534 if ( !urlString.isEmpty() )
535 {
536 mUrl = urlString;
537 }
538 loadHtml( true );
539
540 //since frames had to be created before, we need to emit a changed signal to refresh the widget
541 emit changed();
542 return true;
543}
544
545void QgsLayoutItemHtml::setExpressionContext( const QgsFeature &feature, QgsVectorLayer *layer )
546{
547 mExpressionFeature = feature;
548 mExpressionLayer = layer;
549
550 //setup distance area conversion
551 if ( layer )
552 {
553 mDistanceArea.setSourceCrs( layer->crs(), mLayout->project()->transformContext() );
554 }
555 else if ( mLayout )
556 {
557 //set to composition's mapsettings' crs
558 QgsLayoutItemMap *referenceMap = mLayout->referenceMap();
559 if ( referenceMap )
560 mDistanceArea.setSourceCrs( referenceMap->crs(), mLayout->project()->transformContext() );
561 }
562 if ( mLayout )
563 {
564 mDistanceArea.setEllipsoid( mLayout->project()->ellipsoid() );
565 }
566
567 if ( feature.isValid() )
568 {
569 // create JSON representation of feature
570 QgsJsonExporter exporter( layer );
571 exporter.setIncludeRelated( true );
572 mAtlasFeatureJSON = exporter.exportFeature( feature );
573 }
574 else
575 {
576 mAtlasFeatureJSON.clear();
577 }
578}
579
580void QgsLayoutItemHtml::refreshExpressionContext()
581{
582 QgsVectorLayer *vl = nullptr;
583 QgsFeature feature;
584
585 if ( mLayout )
586 {
587 vl = mLayout->reportContext().layer();
588 feature = mLayout->reportContext().feature();
589 }
590
591 setExpressionContext( feature, vl );
592 loadHtml( true );
593}
594
596{
598
599 //updates data defined properties and redraws item to match
601 {
602 loadHtml( true, &context );
603 }
604}
605
606//JavascriptExecutorLoop
608
609void JavascriptExecutorLoop::done()
610{
611 mDone = true;
612 quit();
613}
614
615void JavascriptExecutorLoop::execIfNotDone()
616{
617 if ( !mDone )
618 exec( QEventLoop::ExcludeUserInputEvents );
619
620 // gross, but nothing else works, so f*** it.. it's not worth spending a day trying to find a non-hacky way
621 // to force the web page to update following the js execution
622 for ( int i = 0; i < 100; i++ )
623 qApp->processEvents();
624}
625
626void JavascriptExecutorLoop::reportError( const QString &error )
627{
628 mDone = true;
629 QgsMessageLog::logMessage( tr( "HTML setFeature function error: %1" ).arg( error ), tr( "Layout" ) );
630 quit();
631}
632
A collection of stubs to mimic the API of a QWebSettings on systems where QtWebkit is not available.
Definition qgswebpage.h:37
@ Millimeters
Millimeters.
Definition qgis.h:5264
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:58
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:87
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())
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