You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyQtWebEngine自定义URL Scheme加载后程序崩溃问题求助

问题原因

崩溃的核心原因是临时QBuffer对象被过早销毁:你在request.reply中直接创建了临时的QBuffer(QByteArray(...)),Python的垃圾回收会在该语句执行完毕后立即回收这个对象,但Qt的WebEngine内核还需要异步读取这个buffer的内容,此时访问已被销毁的内存就会导致程序崩溃。

修复方案

需要保证QBuffer对象的生命周期覆盖整个请求处理过程,以下是两种可靠的实现方式:

方式1:为QBuffer设置父对象

将QBuffer的父对象设置为请求实例,借助Qt的对象树机制自动管理其生命周期,直到请求处理完成:

import sys
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile
from PyQt5.QtWebEngineCore import (
    QWebEngineUrlRequestJob,
    QWebEngineUrlSchemeHandler,
    QWebEngineUrlScheme,
)
from PyQt5.QtCore import QUrl, QByteArray, QBuffer, QIODevice


class SchemeHandler(QWebEngineUrlSchemeHandler):
    def requestStarted(self, request: QWebEngineUrlRequestJob | None) -> None:
        if not request:
            return

        print(f"Got request for {request.requestUrl()}")
        # 创建内容和buffer
        content = QByteArray(b"<h1>Custom Scheme</h1>")
        buffer = QBuffer(content)
        # 必须以只读模式打开buffer,否则WebEngine无法读取内容
        buffer.open(QIODevice.ReadOnly)
        # 设置父对象为request,确保请求完成前buffer不会被销毁
        buffer.setParent(request)
        request.reply(QByteArray(b"text/html"), buffer)


class Browser(QMainWindow):
    def __init__(self) -> None:
        super().__init__()

        browser = QWebEngineView()
        self.setCentralWidget(browser)
        browser.showMaximized()

        self.handler = SchemeHandler()
        profile = QWebEngineProfile.defaultProfile()
        if profile:
            profile.installUrlSchemeHandler(b"test", self.handler)
            print("Installed SchemeHandler")

        browser.load(QUrl("test://someurl"))


def register_scheme():
    scheme = QWebEngineUrlScheme(b"test")
    scheme.setFlags(
        QWebEngineUrlScheme.SecureScheme | QWebEngineUrlScheme.LocalAccessAllowed
    )
    scheme.setSyntax(QWebEngineUrlScheme.Syntax.Path)
    scheme.setDefaultPort(80)
    QWebEngineUrlScheme.registerScheme(scheme)


if __name__ == "__main__":
    register_scheme()

    app = QApplication(sys.argv)
    b = Browser()
    b.show()
    app.exec_()

方式2:将QBuffer保存为Handler的实例属性

如果需要更灵活的生命周期控制,可以将buffer存储为SchemeHandler的属性,只要Handler实例存在,buffer就不会被回收:

class SchemeHandler(QWebEngineUrlSchemeHandler):
    def __init__(self):
        super().__init__()
        self._active_buffers = []  # 存储活跃buffer,防止被垃圾回收

    def requestStarted(self, request: QWebEngineUrlRequestJob | None) -> None:
        if not request:
            return

        print(f"Got request for {request.requestUrl()}")
        content = QByteArray(b"<h1>Custom Scheme</h1>")
        buffer = QBuffer(content)
        buffer.open(QIODevice.ReadOnly)
        self._active_buffers.append(buffer)
        request.reply(QByteArray(b"text/html"), buffer)
        # 请求完成后移除buffer,避免长期内存占用
        request.finished.connect(lambda: self._active_buffers.remove(buffer))
关键注意点
  • 必须调用buffer.open(QIODevice.ReadOnly):QBuffer默认处于关闭状态,不打开的话WebEngine无法读取内容,也可能引发异常。
  • 你的Scheme注册时机正确:在QApplication创建前注册scheme是Qt的强制要求,这部分代码无需修改。

内容的提问来源于stack exchange,提问作者Joa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 05:13:12