Skip to content

fix: migrate QtWebKit API calls to QtWebEngine for QGIS 4 / Qt6 #416

Merged
ghtmtt merged 10 commits intoghtmtt:qt6from
bruno-portfolio:qt6
Mar 31, 2026
Merged

fix: migrate QtWebKit API calls to QtWebEngine for QGIS 4 / Qt6 #416
ghtmtt merged 10 commits intoghtmtt:qt6from
bruno-portfolio:qt6

Conversation

@bruno-portfolio
Copy link
Copy Markdown
Contributor

Hello !

This PR fixes the remaining QtWebKit API calls in the qt6 branch that prevent the plugin from working on QGIS 4.0 / Qt6.

  • PlotLayoutItem crashes silently on creation due to QWebEngineView rejecting a non-QWidget parent
  • save_plot_as_image() crashes on mainFrame() which doesn't exist in QtWebEngine
  • Several unused imports left over from the QtWebKit removal

Related: #377, #400
See also: qgis/QGIS#65398

Architecture change: separated QWebEnginePage (page logic, console logging) from QWebEngineView (offscreen rendering via grab()). The view uses WA_DontShowOnScreen + show() which is the standard Qt6 pattern for offscreen widget rendering.

Full end-to-end testing is blocked by qgis/QGIS#65398 — QGIS 4.0 on Windows does not ship PyQt6-WebEngine bindings. The Qt6 WebEngine DLLs are present but the Python bindings are missing, so QWebEngineView loads but fails to render (loadFinished returns False).

Cheers !

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 30, 2026

many many thanks @bruno-portfolio! I'll test this PR ASAP!

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 30, 2026

@bruno-portfolio print layout is not exploding anymore, and that's a super good thing! However, the plot is not rendered, is that expected?

image

@bruno-portfolio
Copy link
Copy Markdown
Contributor Author

Hi @ghtmtt, thanks for testing !

The blank plot is not expected, it's a timing bug in QtWebEngine. Unlike QtWebKit, loadFinished fires when the DOM is parsed, not when Plotly has finished rendering. So grab() was capturing an empty frame.

I reproduced it in a docker container with qgis/qgis:4.0-trixie + python3-pyqt6.qtwebengine and confirmed

  • grab() immediately after loadFinished → empty (0 pixels)
  • grab() after waiting → rendered content

I just pushed a fix: loading_html_finished now polls for Plotly's DOM elements before calling grab(), with a short compositor delay. Could you test again ?

Cheers !

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 30, 2026

@bruno-portfolio I've tested it again but still the plot is empty. Can I help you debugging in some way?

@bruno-portfolio
Copy link
Copy Markdown
Contributor Author

bruno-portfolio commented Mar 30, 2026

@ghtmtt

what OS/Distro are you using ? does the DataPlotly panel (canvas view, not print layout) render plots correctly? could you check the QGIS log (View → Log Messages → DataPlotly tab) for any JS errors? in the Python console, what does from qgis.PyQt.QtCore import QT_VERSION_STR; print(QT_VERSION_STR) return?

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 30, 2026

I'm on a Debian Sid machine. Plots are created without any issues from the main interface, while I get this error in the log when using from the print layout:

2026-03-30T16:43:16     WARNING    data:text/html;charset=UTF-8,%3Chead%3E%3Cmeta%20charset%3D%22utf-8%22%20%2F%3E%3Cscript%20src%3D%22file%3A%2F%2F%2Fhome%2Fmatteo%2Flavori%2Ffaunalia%2Ffaunalia_git%2FDataPlotly%2FDataPlotly%2Fjsscripts%2Fpolyfill.min.js%22%3E%3C%2Fscript%3E%3Cscript%20src%3D%22file
....................lots of other lines................
Uncaught TypeError: plotly_div.on is not a function

…creates an opaque Chromium origin that silently blocks file:// script loading (Plotly.js).
@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 30, 2026

I really think we are almost there. The previous error is gone, but I get this other one:

2026-03-30T17:08:02     WARNING    file:///tmp/tmp8op4ehnh.html:7 Uncaught ReferenceError: qt is not defined

@bruno-portfolio
Copy link
Copy Markdown
Contributor Author

@ghtmtt That error is pre-existing. But is the plot actually rendering now in the layout?

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 30, 2026

nope

image

@bruno-portfolio
Copy link
Copy Markdown
Contributor Author

Try now and let me know

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 30, 2026

still no, but something new is appearing on the plot canvas:

image

@bruno-portfolio
Copy link
Copy Markdown
Contributor Author

Let's try this then

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 30, 2026

the plot lines disappear. Can I help you in some other ways?

image

@bruno-portfolio
Copy link
Copy Markdown
Contributor Author

@ghtmtt, sorry for the back and forth ! Could you run this in the QGIS Python console after adding a plot item to the print layout ?

item = [i for i in iface.openLayoutDesigners()[0].layout().items() if hasattr(i, 'web_page')][0]; item.web_page.runJavaScript("JSON.stringify({plotly: typeof Plotly, scatter: !!document.querySelector('.scatterlayer'), points: document.querySelectorAll('.point').length, flag: window._plotlyRenderComplete, data: document.querySelector('.js-plotly-plot') ? document.querySelector('.js-plotly-plot').data.length : -1})", print)

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 31, 2026

thanks that you are taking the time to fix this! That's what I get from the console:

{"plotly":"object","scatter":true,"points":30,"flag":true,"data":1}

@bruno-portfolio
Copy link
Copy Markdown
Contributor Author

bruno-portfolio commented Mar 31, 2026

Replaced web_view.grab() with Plotly.toImage(), the Chromium compositor doesn't reliably update the backing store for offscreen widgets, so we now let Plotly export the image directly as PNG base64 and decode it into a QPixmap on the Python side.

Let me know what happens

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 31, 2026

almost here! Plot is displayed but it is not resizing when changing the frame size. Not only when created, but also if resized manually after the creation:

image

@bruno-portfolio
Copy link
Copy Markdown
Contributor Author

Let's see now

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 31, 2026

plot is not displayed anymore:

image

@bruno-portfolio
Copy link
Copy Markdown
Contributor Author

can u run this item = [i for i in iface.openLayoutDesigners()[0].layout().items() if hasattr(i, 'web_page')][0]; item.web_page.runJavaScript("var p = document.querySelector('.js-plotly-plot'); JSON.stringify({clientW: document.documentElement.clientWidth, clientH: document.documentElement.clientHeight, plotW: p.offsetWidth, plotH: p.offsetHeight, fullLayoutW: p._fullLayout ? p._fullLayout.width : -1, fullLayoutH: p._fullLayout ? p._fullLayout.height : -1})", print) after adding the plot please?

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 31, 2026

here you go:

{"clientW":13464,"clientH":10224,"plotW":13448,"plotH":10208,"fullLayoutW":13448,"fullLayoutH":10208}

@bruno-portfolio
Copy link
Copy Markdown
Contributor Author

alright, let's test this one

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 31, 2026

yeah! It's a little bit grainy, but resizing is working!

image

@bruno-portfolio
Copy link
Copy Markdown
Contributor Author

That's great !!!
Would you like me to reduce the widget size and rebalance the scale to see if we can fix the grainy thing ?

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 31, 2026

if you want to include also the resizing in this PR it would be super great!

@bruno-portfolio
Copy link
Copy Markdown
Contributor Author

done

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 31, 2026

awesome! Super many thanks for this contribution. I'm going to merge the pull request in the qt6 branch, but I've still to figure out a decent way to ship the plugin for both QGIS 3 with Webkit and QGIS 4 with Webengine.

That's a super great contribution, thank you so much!

@bruno-portfolio
Copy link
Copy Markdown
Contributor Author

Thanks @ghtmtt !

For the dual QGIS 3/4 shipping, one approach would be a runtime check, try importing QtWebEngineWidgets, fall back to QtWebKitWidgets, with the layout item using the appropriate rendering path (the old mainFrame().render() for WebKit, Plotly.toImage() for WebEngine).

happy to help with that if you want !
let me know.

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 31, 2026

yeah, that is also what I was thinking. Also the js function for the plot interaction is different and I've added a custom bridge class that is needed only for webengine. If you want to help me out we can split the work!

@ghtmtt ghtmtt merged commit 4ecb178 into ghtmtt:qt6 Mar 31, 2026
0 of 3 checks passed
@bruno-portfolio
Copy link
Copy Markdown
Contributor Author

Sure, let's do it !

I can take the layout item dual rendering path (WebKit mainFrame().render() vs WebEngine Plotly.toImage()) with runtime detection. You handle the interaction bridge + JS callbacks side.

We can track it under #377 since that's already the migration issue.
Want to coordinate there?

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 31, 2026

good to me! We can work on separated branches starting from this last one and when we are done we can merge to master directly. Do you have both QGIS releases installed?

The strategy could be to check the QGIS version and if < 4 than using WebKit (as it is right now) is >= 4 then using WebEngine, without considering to have the chance to have a QGIS 4 with WebKit and not WebEngine. What do you think?

@bruno-portfolio
Copy link
Copy Markdown
Contributor Author

I only have QGIS 4.0 on windows + docker for testing. I don't have QGIS 3 installed, so I can't test the webkit path directly, I'll need your help validating that side.

I went with import-based detection instead of version check, try: import QtWebEngine / except: import qtwebkit. In practice the result is identical to checking the QGIS version, but it also handles edge cases like missing bindings gracefully instead of crashing.

If you prefer a version check, it's a trivial change in one file. What do you think?

I've already started working on the detection module + layout item dual rendering on a branch. I'll open a PR shortly so you can see the approach and test the webkit path on your end if that is ok with you.

@ghtmtt
Copy link
Copy Markdown
Owner

ghtmtt commented Mar 31, 2026

I can test on my linux with QGIS 3 and 4. I've prepared a PR feel free to push to that branch or to a clean one and then we will merge into the main qt6 and finally into the master one (resolving all the conflicts first :( )

#420

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants