5

I want to take a screenshot of QWebEngineView for a page with a large image. When running code below red background is rendered correctly but the image is not.

How can I ensure the page is fully rendered before taking a screenshot?

from PySide2 import QtCore, QtWidgets, QtWebEngineWidgets

html = """
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <style>
  img { max-width:100%; max-height:100%; }
  div { height: 270px; width: 540px; }
  body { margin: 0; background-color: red; }
  </style>
</head>
<body>
<div>
  <img src="https://eoimages.gsfc.nasa.gov/images/imagerecords/73000/73751/world.topo.bathy.200407.3x5400x2700.png">
</div>
</body>
</html>
"""

class Capture(QtWebEngineWidgets.QWebEngineView):
  def __init__(self, parent=None):
    super(Capture, self).__init__(parent)

    self.loadFinished.connect(self.save)
    self.show()
    self.resize(540, 270+5)

  @QtCore.Slot(bool)
  def save(self, isOk):
    if not isOk:
      print('Error')
      return

    self.grab().save('page.png', b'PNG')

if __name__ == '__main__':
  app = QtWidgets.QApplication()
  capture = Capture()
  capture.setHtml(html)
  app.exec_()

EDIT:

Equivalent code snippet in C++: https://pastebin.com/cH7ZgFDB

EDIT 2:

I have opened ticket in Qt bug tracking system.

  • 1
    In my case the image is progressively showing but when a certain threshold passes the image disappears, which I suspect is that the image is so heavy that Qt WebEngine does not support it, I am looking at the docs the limit of the size of the images that can be handled Qt or Qt WebEngine. – eyllanesc May 22 '19 at 18:54
  • 1
    For example in X11 QPixmap is limited to 32768x32768 https://code.qt.io/cgit/qt/qtbase.git/tree/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp#n492 – eyllanesc May 22 '19 at 19:01
  • 1
    @eyllanesc The image I am using in the code sample is only 5400 × 2700 so it is within QPixmap limits. I have experienced issues with smaller images, it is simply easier to reproduce the issue with larger image. – Krzysztof Sakowski May 23 '19 at 06:07
  • 1
    The limit that indicates only a reference, probably Qt WebEngine has other limits, later I will test the example in C++ to rule out that the problem is the binding of PySide2, if so it is probably a bug or limitation of Qt WebEngine – eyllanesc May 23 '19 at 06:10
  • 2
    @eyllanesc I have been trying adding a timer of 300ms between loadFinished signal and save function and it helps for this code snippet but it does not look like a reliable solution. I am looking for some kind of signal or event stating "view is ready you can take the screenshot now". – Krzysztof Sakowski May 23 '19 at 06:55
  • 1
    If you react on `loadFinished()`, your issue might be caused due to snapshot-ing before rendering is completed. This sounds to me, like there are still pending update events in the event queue when you make the snapshot. If that's true, the solution is not that complicated: Make the snapshot via a single-shot timer (with timeout 0) because this should appear at end of queue i.e. after all pending rendering events. (I must admit that I've no experience with QWebEngine - just some experience with Qt in general.) – Scheff's Cat May 23 '19 at 16:06
  • 1
    FYI: @eyllanesc C++ version of the code snippet: https://pastebin.com/cH7ZgFDB – Krzysztof Sakowski May 24 '19 at 06:57
  • 1
    @Scheff thanks for the tip. I tried that. Unfortunately 0 does not fix the issue. I was able to get correct results with ~300ms, but that is not really a solution. – Krzysztof Sakowski May 24 '19 at 07:00
  • 1
    I had a look onto the doc. of `QWebEngine`. It seems there is nothing that is signaled after rendering has been completed. (The only thing I found was `QWebEngineView::renderProcessTerminated()` which isn't helpful for your issue.) I'm afraid that there is no such thing because, actually, there is no such point as a web page may periodically be updated (e.g. for rendering gif animations). Hence, your 300 ms might be the best you can get. (Tracking paintEvents is probably as good or bad for the same reasons.) – Scheff's Cat May 24 '19 at 09:31
  • 1
    Did you try with JavaScript .. https://stackoverflow.com/questions/33467776/qt-qwebengine-render-after-scrolling – Mohammad Kanan May 30 '19 at 01:36
  • 1
    https://stackoverflow.com/questions/821516/browser-independent-way-to-detect-when-image-has-been-loaded – Mohammad Kanan May 30 '19 at 01:37
  • 1
    @MohammadKanan I tried similar solution with intercepting Qt paint events from https://stackoverflow.com/questions/30566101/how-can-i-get-paint-events-with-qtwebengine and it did not work. – Krzysztof Sakowski May 31 '19 at 14:36
  • You can try with `QWebEnginePage::contentsSizeChanged`. It is emitted two or more times during rendering with different size in the parameter. The first time the size is equal to the initial view size. – Ivan Oct 24 '19 at 07:19

0 Answers0