有什么方法可以同步调用 QWebEnginePage 对象的方法 'toHtml' 吗?

Is there any way to call synchronously the method 'toHtml' which is QWebEnginePage's object?

我正在尝试从 QWebEnginePage 对象获取 html 代码。根据 Qt 参考,QWebEnginePage 对象的 'toHtml' 是异步方法,如下所示。

Asynchronous method to retrieve the page's content as HTML, enclosed in HTML and BODY tags. Upon successful completion, resultCallback is called with the page's content.

所以我试图找出如何同步调用此方法。

下面是我想要得到的结果

class MainWindow(QWidget):
  html = None
  ...
  ...
  def store_html(self, data):
    self.html = data

  def get_html(self):
    current_page = self.web_view.page()
    current_page.toHtml(self.store_html)
    # I want to wait until the 'store_html' method is finished
    # but the 'toHtml' is called asynchronously, return None when try to return self.html value like below.
    return self.html 
  ...
  ...

获得该行为的一种简单方法是使用 QEventLoop()。此 class 的对象阻止执行 exec_() 之后的代码,这并不意味着 GUI 不会继续工作。

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtWebEngineWidgets import *


class Widget(QWidget):
    toHtmlFinished = pyqtSignal()

    def __init__(self, *args, **kwargs):
        QWidget.__init__(self, *args, **kwargs)
        self.setLayout(QVBoxLayout())
        self.web_view = QWebEngineView(self)
        self.web_view.load(QUrl("http://doc.qt.io/qt-5/qeventloop.html"))
        btn = QPushButton("Get HTML", self)
        self.layout().addWidget(self.web_view)
        self.layout().addWidget(btn)
        btn.clicked.connect(self.get_html)
        self.html = ""

    def store_html(self, html):
        self.html = html
        self.toHtmlFinished.emit()

    def get_html(self):
        current_page = self.web_view.page()
        current_page.toHtml(self.store_html)
        loop = QEventLoop()
        self.toHtmlFinished.connect(loop.quit)
        loop.exec_()
        print(self.html)


if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

注意:同样的方法适用于PySide2。

您可以使用在 multiprocessing.Pipe 的发送方法的一侧创建的 multiprocessing.Connection 对象作为回调,然后立即使用管道的另一端的 recv 方法。 Recv 将阻塞直到收到 html,所以请记住这一点

示例:

from multiprocessing import Pipe

class MainWindow(QWidget):
    def __init__(...):
        ...
        self.from_loopback,self.to_loopback=Pipe(False)

    def get_html(self):
        current_page = self.web_view.page()
        current_page.toHtml(self.to_loopback.send)
        return self.from_loopback.recv() 

QEventLoop 方法

相比,这里有一种不同的方法和不同的行为

您可以从 QWebEngineView 继承并使用 loadFinished Signal 扩展 load() 功能并创建自定义方法 read_html()

class MyWebView(QWebEngineView):

    def __init__(self, parent):
        super(MyWebView, self).__init__(parent)
        self.html = None

    def read_html(self, url):
        """
        Load url and read webpage content in html
        """
        def read_page():
            def process_html(html):
                self.html = html
            self.page().toHtml(process_html)

        self.load(url)
        self.loadFinished.connect(read_page)

这样应用程序就不会在等待页面在事件循环中完成加载时暂停,但是一旦页面加载完毕,您就可以访问 HTML 内容。

class MainWindow(QWidget):
    def __int__(self):
        ...
        self.web_view = MyWebView(self)
        self.web_view.read_html(r'https://www.xingyulei.com/')
        ...
        self.btn.clicked.connect(self.print_html)

    def print_html(self):
        print(self.view.html)