不依赖PySide/PyQt,用pybind11暴露Qt6 WebView功能给Python
Qt6 WebView与Python集成问题(基于pybind11)
需求与预期目标
- 动态控制WebView窗口:调整尺寸、全屏、无边框等样式设置
- 支持加载本地HTML内容和远程URL
- 实现Python与WebView中JavaScript的双向交互(调用JS函数、获取返回结果)
- 不依赖PySide/PyQt,规避许可限制
已尝试方案及问题
使用pybind11封装C++类包裹QApplication和QWebEngineView,代码如下:
#include <pybind11/stl.h> #include <QApplication> #include <QWebEngineView> #include <QString> namespace py = pybind11; class WebView { public: WebView() { int argc = 0; char *argv[] = {nullptr}; app = std::make_unique<QApplication>(argc, argv); view = std::make_unique<QWebEngineView>(); } void resize(int width, int height) { view->resize(width, height); } void setHtml(const std::string &html) { view->setHtml(QString::fromStdString(html)); } void setUrl(const std::string &url) { view->setUrl(QUrl(QString::fromStdString(url))); } void exec() { view->show(); app->exec(); } private: std::unique_ptr<QApplication> app; std::unique_ptr<QWebEngineView> view; }; PYBIND11_MODULE(qt_webview, m) { py::class_<WebView>(m, "WebView") .def(py::init<>()) .def("resize", &WebView::resize) .def("setHtml", &WebView::setHtml) .def("setUrl", &WebView::setUrl) .def("exec", &WebView::exec); }
编译正常,但Python导入时出现错误:ImportError: /home/red-x/Documents/GUIEngine/MyQtWebViewApp/qt_webview.cpython-312-x86_64-linux-gnu.so: undefined symbol: qt_version_ta
同时存在以下功能缺失:
- 无法调整窗口样式(如无边框、全屏)
- 无法实现Python与JavaScript的交互
- 现有功能无法满足全部需求
约束条件
- 禁止使用PySide/PyQt
- 必须使用pybind11作为绑定工具
核心问题
- 如何通过pybind11将Qt窗口样式、WebView JS交互等功能暴露给Python?
- 有无不依赖PySide/PyQt的更优集成方式?
- 有无类似实现的示例或开源项目?
环境信息
OS: Windows 10 或 Ubuntu 20.04 Qt版本: 6.4.2 Python版本: 3.12 编译器: 支持C++17的g++
解决方案
1. 修复导入错误(undefined symbol: qt_version_ta)
该错误源于编译时未正确链接Qt库,需确保编译脚本/CMake配置中引入Qt6核心模块:
CMakeLists.txt示例:
cmake_minimum_required(VERSION 3.16) project(qt_webview) set(CMAKE_CXX_STANDARD 17) set(CMAKE_PREFIX_PATH "/path/to/qt6") # 替换为你的Qt6安装路径 find_package(Qt6 REQUIRED COMPONENTS Core Widgets WebEngineWidgets) find_package(pybind11 REQUIRED) pybind11_add_module(qt_webview qt_webview.cpp) target_link_libraries(qt_webview PRIVATE Qt6::Core Qt6::Widgets Qt6::WebEngineWidgets )
g++直接编译命令:
g++ -O3 -Wall -shared -std=c++17 `python3 -m pybind11 --includes` qt_webview.cpp -o qt_webview.so `pkg-config --cflags --libs Qt6Core Qt6Widgets Qt6WebEngineWidgets`
2. 扩展窗口样式控制功能
修改C++类,添加窗口样式控制方法,并通过pybind11暴露给Python:
更新后的C++类代码:
#include <QtWidgets/QWidget> // 添加头文件 // 在WebView类中新增方法 void setFrameless() { view->setWindowFlags(Qt::FramelessWindowHint); } void setFullScreen() { view->showFullScreen(); } void setWindowTitle(const std::string &title) { view->setWindowTitle(QString::fromStdString(title)); } void showNormal() { view->showNormal(); }
更新pybind11绑定代码:
PYBIND11_MODULE(qt_webview, m) { py::class_<WebView>(m, "WebView") .def(py::init<>()) .def("resize", &WebView::resize) .def("setHtml", &WebView::setHtml) .def("setUrl", &WebView::setUrl) .def("exec", &WebView::exec) .def("setFrameless", &WebView::setFrameless) .def("setFullScreen", &WebView::setFullScreen) .def("setWindowTitle", &WebView::setWindowTitle) .def("showNormal", &WebView::showNormal); }
3. 实现Python与JavaScript交互
利用QWebEnginePage::runJavaScript结合pybind11的回调机制,实现异步JS调用与结果返回:
更新后的C++类代码:
#include <pybind11/functional.h> // 添加头文件 // 在WebView类中新增方法 void runJavaScript(const std::string &script, py::function callback) { view->page()->runJavaScript(QString::fromStdString(script), [callback](const QVariant &result) { // 将QVariant转换为Python兼容类型 if (result.isNull()) { callback(nullptr); } else if (result.type() == QVariant::String) { callback(result.toString().toStdString()); } else if (result.type() == QVariant::Int) { callback(result.toInt()); } else if (result.type() == QVariant::Double) { callback(result.toDouble()); } else if (result.type() == QVariant::Bool) { callback(result.toBool()); } else { // 其他类型可按需扩展 callback(nullptr); } }); }
更新pybind11绑定代码:
PYBIND11_MODULE(qt_webview, m) { // ... 原有绑定 .def("runJavaScript", &WebView::runJavaScript); }
Python调用示例:
from qt_webview import WebView wv = WebView() wv.setHtml("<html><body><script>function add(a,b){return a+b;}</script></body></html>") # 调用JS函数并获取结果 def on_result(res): print("JS返回结果:", res) wv.runJavaScript("add(2,3)", on_result) wv.exec()
4. 替代方案与开源参考
- Qt for Python LGPL版本:若仅需规避GPL,Qt for Python(PySide6)的LGPL版本合规,只要不修改Qt源码或静态链接即可。
- 轻量绑定工具:
qtpybind(基于pybind11的Qt绑定生成工具)可自动生成绑定代码,减少手动封装工作量。 - 非Qt替代方案:
cefpython(基于Chromium Embedded Framework),无需依赖Qt,但体积较大。
内容的提问来源于stack exchange,提问作者red X
相关产品推荐
相关产品推荐

