Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QWebEngineView cannot load large SVG/HTML using setHtml

I tried two different ways to load and show an SVG file in PyQt:

import sys
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QUrl

app = QApplication(sys.argv)
webView = QWebEngineView()

# Variant 1: Reasonably fast
webView.load(QUrl('file:///Test.svg'))

# Variant 2: Slow for small files, not working for big ones
webView.setHtml('<svg>........')

webView.show()
sys.exit(app.exec_())

The first way works okay but requires a file as input. I would like to generate the SVGs dynamically though, so this is not really an option. Does anyone have an idea why the second approach is so darn slow or failing completely for more complex vector images?

like image 415
sonovice Avatar asked Oct 27 '25 14:10

sonovice


1 Answers

UPDATE:

A work-around for this issue is to use a custom url scheme handler, which bypasses the need to pass in the file contents via a data-url.

Below is a basic demo which shows how implement this. This works with both PyQt5 and PyQt6 (just amend the imports):

from PyQt5.QtCore import (
    QUrl, QByteArray, QBuffer, QIODevice,
)
from PyQt5.QtWidgets import (
    QApplication, QWidget, QPushButton, QCheckBox, QGridLayout,
)
from PyQt5.QtWebEngineCore import (
    QWebEngineUrlScheme, QWebEngineUrlSchemeHandler, QWebEngineUrlRequestJob,
    )
from PyQt5.QtWebEngineWidgets import QWebEngineView


HTML_DATA = {}
URL_SCHEME = 'local'
TEST_FILE = 'test.data'


class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
    def requestStarted(self, job):
        href = job.requestUrl().path()
        if (data := HTML_DATA.get(href)) is not None:
            if not isinstance(data, bytes):
                data = str(data).encode()
            mime = QByteArray(b'text/html')
            buffer = QBuffer(job)
            buffer.setData(data)
            buffer.open(QIODevice.OpenModeFlag.ReadOnly)
            job.reply(mime, buffer)
        else:
            print(f'ERROR: request job failed: {href!r}')
            job.fail(QWebEngineUrlRequestJob.Error.UrlNotFound)


class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.button = QPushButton('Load Source')
        self.button.clicked.connect(self.handleButtton)
        self.option = QCheckBox('Use url scheme handler')
        self.view = QWebEngineView()
        self.view.page().profile().installUrlSchemeHandler(
            bytes(URL_SCHEME, 'ascii'), UrlSchemeHandler(self))
        self.view.loadFinished.connect(self.handleLoaded)
        layout = QGridLayout(self)
        layout.addWidget(self.view, 0, 0, 1, 2)
        layout.addWidget(self.button, 1, 0)
        layout.addWidget(self.option, 1, 1)

    def handleButtton(self):
        if self.option.isChecked():
            url = QUrl(TEST_FILE)
            url.setScheme(URL_SCHEME)
            self.view.setUrl(url)
        else:
            self.view.setHtml(HTML_DATA[TEST_FILE])

    def handleLoaded(self, ok):
        if not ok:
            self.view.setHtml('<h3>414: URI Too Long</h3>')


if __name__ == '__main__':

    try:
        HTML_DATA[TEST_FILE] = open('large.svg').read()
    except OSError:
        HTML_DATA[TEST_FILE] = (html := f"""
            <html><head></head><body>
            <h3>Test Page ({{}} MB)</h3>{'''<p>
            Lorem ipsum dolor sit amet, consectetur adipiscing
            elit, sed do eiusmod tempor incididunt ut labore et
            dolore magna aliqua. Ut enim ad minim veniam, quis
            nostrud exercitation ullamco laboris nisi ut aliquip
            ex ea commodo consequat. Duis aute irure dolor in
            reprehenderit in voluptate velit esse cillum dolore
            eu fugiat nulla pariatur. Excepteur sint occaecat
            cupidatat non proident, sunt in culpa qui officia
            deserunt mollit anim id est laborum.
            </p>''' * 4000}</body></html>"""
            ).format(round(len(html) / 1e6, 1))

    scheme = QWebEngineUrlScheme(bytes(URL_SCHEME, 'ascii'))
    scheme.setFlags(QWebEngineUrlScheme.Flag.SecureScheme |
                    QWebEngineUrlScheme.Flag.LocalScheme |
                    QWebEngineUrlScheme.Flag.LocalAccessAllowed)
    QWebEngineUrlScheme.registerScheme(scheme)

    app = QApplication(['Test'])
    window = Window()
    window.setGeometry(600, 50, 800, 600)
    window.show()
    app.exec()

PREVIOUS ANSWER:

The setHtml function cannot load any content (not just svg) that is greater than 2MB in size. This is because Chromium uses a data: scheme url to load the content (which obvioulsy limits the size to the maximum length for a url). So it would seem that the only option is to load the svg from a local file.

  • See: QTBUG-53414
like image 172
ekhumoro Avatar answered Oct 29 '25 04:10

ekhumoro



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!