使用QWebEngineView显示html时如何获取verticalScrollBar的最大值和当前值

Posted

技术标签:

【中文标题】使用QWebEngineView显示html时如何获取verticalScrollBar的最大值和当前值【英文标题】:How can I get the maximum and current value of the verticalScrollBar when displaying html with QWebEngineView 【发布时间】:2019-03-21 23:33:23 【问题描述】:

我正在尝试在 Maya(Python 2.7.11 和 Maya Qt 5.6.1. 和 PySide2)中创建一个 UI,其中“确定”按钮显示为灰色,直到用户滚动到底部。我可以通过从verticalScrollBar 中获取值和最大值来使用QTextEdit 轻松完成此操作,但我似乎无法在QWebEngineView 或QWebEnginePage 中找到类似的对象。有什么想法吗?

【问题讨论】:

哦,我以为我在星期五留下了评论。我能够运行代码,但遇到了一个破坏滚动检测功能的 unicode 错误。另一个问题是我真的不想使用 jinja2 并且由于正在加载的页面是本地文件,我可以将 javascript 嵌入页面中,而不是在加载时将其即时添加到页面。跨度> 这是错误:code ('Finished loading: ', True) Traceback (most recent call last): File "scrool.py", line 69, in onLoadFinished self.load_qwebchannel() File "scrool.py", line 77, in load_qwebchannel self.runJavaScript(content.data().decode()) UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 152: ordinal not in range(128) Maya 使用 Python 2 您提供的网址有错误 在 Maya 推出使用 3 的版本之前,我将坚持使用 2.7,但我也没想到这项特殊任务会像事实证明的那样棘手。 【参考方案1】:

如果您想从 DOM 获取一些信息,您必须使用 javascript 来完成,在这种情况下,我们将创建一个包含所需信息的对象并将其导出到 html,以便每次滚动状态更改它将被更新。

from PySide2 import QtCore, QtWidgets, QtWebEngineWidgets, QtWebChannel
from jinja2 import Template

class Element(QtCore.QObject):
    def __init__(self, name, parent=None):
        super(Element, self).__init__(parent)
        self._name = name

    @property
    def name(self):
        return self._name

    def script(self):
        return ''

class YScrollBarListener(Element):
    valueChanged = QtCore.Signal(int)
    maximumChanged = QtCore.Signal(int)

    def __init__(self, name, parent=None):
        super(YScrollBarListener, self).__init__(name, parent)
        self._value = -1
        self._maximum = -1

    @QtCore.Property(int, notify=valueChanged)
    def value(self):
        return self._value

    @QtCore.Property(int, notify=maximumChanged)
    def maximum(self):
        return self._maximum

    def script(self):
        _script = '''
        window.addEventListener("scroll", function(event)
            name.update_value(this.scrollY);
            // https://***.com/a/43571388/6622587
            var scrollMaxY = window.scrollMaxY || (document.documentElement.scrollHeight - document.documentElement.clientHeight)
            name.update_maximum(scrollMaxY)
        );
        '''
        return Template(_script).render(name=self.name)

    @QtCore.Slot(int)
    def update_value(self, value):
        if self._value != value:
            self._value = value
            self.valueChanged.emit(value)

    @QtCore.Slot(int)
    def update_maximum(self, maximum):
        if self._maximum != maximum:
            self._maximum = maximum
            self.maximumChanged.emit(maximum)

class WebEnginePage(QtWebEngineWidgets.QWebEnginePage):
    def __init__(self, *args, **kwargs):
        super(WebEnginePage, self).__init__(*args, **kwargs)
        self.loadFinished.connect(self.onLoadFinished)
        self._objects = []

    def add_object(self, obj):
        self._objects.append(obj)

    @QtCore.Slot(bool)
    def onLoadFinished(self, ok):
        print("Finished loading: ", ok)
        if ok:
            self.load_qwebchannel()
            self.add_objects()

    def load_qwebchannel(self):
        file = QtCore.QFile(":/qtwebchannel/qwebchannel.js")
        if file.open(QtCore.QIODevice.ReadOnly):
            content = file.readAll()
            file.close()
            self.runJavaScript(content.data().decode())
        if self.webChannel() is None:
            channel = QtWebChannel.QWebChannel(self)
            self.setWebChannel(channel)

    def add_objects(self):
        if self.webChannel() is not None:
            objects = obj.name : obj for obj in self._objects
            self.webChannel().registerObjects(objects)
            _script = '''
            % for obj in objects %
            var obj;
            % endfor %
            new QWebChannel(qt.webChannelTransport, function (channel) 
            % for obj in objects %
                obj = channel.objects.obj;
            % endfor %
            ); 
            '''
            self.runJavaScript(Template(_script).render(objects=objects.keys()))
            for obj in self._objects:
                if isinstance(obj, Element):
                    self.runJavaScript(obj.script())

class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)

        self._scrollbar_listener = YScrollBarListener("y_scrollbar_listener", self)
        self._scrollbar_listener.valueChanged.connect(self.on_y_value_changed)
        view = QtWebEngineWidgets.QWebEngineView()
        page = WebEnginePage(view)
        page.add_object(self._scrollbar_listener)
        view.setPage(page)

        view.load(QtCore.QUrl("https://***.com/questions/43282899"))

        self._button = QtWidgets.QPushButton("button")
        progressbar = QtWidgets.QProgressBar(maximum=100)
        view.loadProgress.connect(progressbar.setValue)

        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(view)
        lay.addWidget(self._button)
        lay.addWidget(progressbar)

        self.resize(640, 480)

    def on_y_value_changed(self, value):
        self._button.setEnabled(self._scrollbar_listener.maximum != value)


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    app.setStyle('fusion')
    app.setStyleSheet('''
    QPushButton
    
        background-color: #2E8B57;
    
    QPushButton:disabled
    
        background-color: #FFFAFA;
    
    ''')
    w = Widget()
    w.show()
    sys.exit(app.exec_())

【讨论】:

以上是关于使用QWebEngineView显示html时如何获取verticalScrollBar的最大值和当前值的主要内容,如果未能解决你的问题,请参考以下文章

如何在 QWebEngineView 中禁用工具提示(标题)的自动换行

如何在使用 QWebEngineView 'loadFinished' 加载页面后立即更改 html 元素?

使用 QWebEngineView 时如何防止创建奇怪的文件夹?

如何在 QWebEngineView 周围显示边框?

如何在 QWebEngineView 中设置背景图像 [重复]

PyQt5 QWebEngineView 无法正常工作