如何使用 QWebEngineView 和 qtwebchannel.js 逐步加载 javascript?
Posted
技术标签:
【中文标题】如何使用 QWebEngineView 和 qtwebchannel.js 逐步加载 javascript?【英文标题】:How to load javascript step by step with QWebEngineView and qtwebchannel.js? 【发布时间】:2019-02-05 15:55:35 【问题描述】:我的设置就像一个在后端使用 python 并在前端使用 html/css/js 的框架。我的问题是在加载 QWebEngineView 期间出现的。
我在网上搜索了如何使用QWebEngineView建立python和javascript之间的通信,最后我尝试使用QtWebChannel。 所以我设置了一切,一切都很好地与python和javascript之间的通信,但出现了下一个问题:
首先:我不能直接在带有标签<script>
的html中加载javascript文件
第二:javascript随机加载,我尝试使用my_view.page().runJavascript(my_js)
从python加载javascript,但一次尝试两次。所以有时 jQuery 会在最后加载,所以代码的其他部分不起作用。
base.html:
<!DOCTYPE html>
<html lang="en">
<p id="log"></p>
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script>
window.onerror = function (error, url, line)
console.error("ERROR: " + error.toString());
console.error("LINE: " + line.toString());
;
function load_app()
new QWebChannel(qt.webChannelTransport, function (channel)
window.app = channel.objects.app;
app.load_javascript(function(ret)
console.error("load javascript: " + ret)
);
);
load_app();
console.error("app loaded")
</script>
application_html_content | safe
</html>
HTML的另一部分:
% extends 'base.html' %
% block content %
<div class="row">
% for user_id, user in user_dict.items() %
<div id=" user_id " class="col s12 m6">
<div class="card blue-grey darken-1">
<div class="card-content white-text">
<span class="card-title">Visit Card</span>
<p> user.name </p>
</div>
<div class="card-action">
<button id="btn_del_ user_id " class="btn blue waves-effect waves-light" onclick="delete_user( user_id )">Delete</button>
<button class="btn blue waves-effect waves-light" onclick="detail_user( user_id )">Detail</button>
</div>
</div>
</div>
% endfor %
</div>
% endblock %
% block javascript %
<script>
$(document).ready(function()
app.request_result.connect(function (result)
if ("delete" in result)
user_id = result.delete;
var element = document.getElementById(user_id);
element.parentNode.removeChild(element)
);
console.error("ready");
);
function delete_user(user_id)
document.getElementById("btn_del_" + user_id).innerHTML = "Waiting ...";
app.request('DemoHtml:Default:delete', user_id);
function detail_user(user_id)
app.path('detail_user', "user_id": user_id);
</script>
% endblock %
load_javascript 函数:
JQUERY = "vendor/Resources/js/jquery.js"
MATERIALIZE = "vendor/Resources/css/materialize/js/materialize.js"
@pyqtSlot(result=str)
def load_javascript(self):
with open(os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), self.MATERIALIZE), "r") as m_stream:
materialize_content = m_stream.read()
with open(os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), self.JQUERY), "r") as j_stream:
jquery_content = j_stream.read()
self.template_view.view.page().runJavaScript(jquery_content)
self.template_view.view.page().runJavaScript(materialize_content)
return "ok"
如您所见,通常我必须在日志中看到错误:
首先:“加载javascript:ok” 第二个:“应用已加载”但一次一二,这是相反的:
js:应用已加载 js: 错误: 未捕获的 ReferenceError: $ 未定义 js:线路:67 js: Uncaught ReferenceError: $ is not defined js: 加载 javascript: ok对此有什么帮助吗?
提前感谢您!
【问题讨论】:
您可能需要等待脚本被执行?有一个callback function runJavaScript 是异步的,所以必须等待结果才能再次调用。 @MauriceMeyer 所以当我的函数load_javascript()
被调用时,我可以在runJavascript()
中设置回调来强制加载等待结束的页面? @ekhumoro 我如何等待结果? runJavaScript()
只需加载 jQuery
@FlorianCouderc 如链接文档中所述,传递给runJavascript
的回调将在返回时被调用。所以只需将runJavascript
调用链接在一起,而不是一个接一个地调用它们。您还可以创建一个带有本地事件循环的包装函数,该函数在调用回调之前一直阻塞。这将有效地制作runJavascript
的同步版本。
【参考方案1】:
我解决了我的问题,感谢@ekhumoro 试图帮助我,我在这个帖子上找到了答案:
如何等待另一个JS加载进行操作?:https://***.com/a/8618519/8293533
所以为了让它工作,我将我的 javascript 更改为:
我将此文件命名为app.js
function set_app()
try
new QWebChannel(qt.webChannelTransport, function (channel)
window.app_channel = channel.objects.app;
);
catch (e)
console.error("setting_app error: " + e)
set_app();
function request(route, args)
let interval = 10;
window.setTimeout(function ()
if (window["app_channel"])
app_channel.request(route, args)
else
try
set_app();
catch(error)
console.error("app load error: " + error)
window.setTimeout(arguments.callee, interval);
, interval)
function path(route, args)
let interval = 10;
window.setTimeout(function ()
if (window["app_channel"])
app_channel.path(route, args)
else
try
set_app();
catch(error)
console.error("app load error: " + error)
window.setTimeout(arguments.callee, interval);
, interval)
function request_result(callback)
let interval = 10;
window.setTimeout(function ()
if (window["app_channel"])
app_channel.request_result.connect(callback)
else
try
set_app();
catch(error)
console.error("app load error: " + error)
window.setTimeout(arguments.callee, interval);
, interval)
我在python中删除了我的代码load_javascript
,因为我找到了使用<script>
标签和qrc:///
路径调用js的方法。
现在我的 html 头看起来像这样:
<!DOCTYPE html>
<html lang="en">
<p id="log"></p>
<script src="qrc:///qwebchannel.js"></script>
<script src="qrc:///app.js"></script>
<script src="qrc:///jquery.js"></script>
application_html_content | safe
<script src="qrc:///materialize.min.js"></script>
</html>
要使用qrc:///xxx.js
,我使用了QResource
和.qrc
、.rcc
文件。
这是我的代码示例,供那些想要的人使用:
class ApplicationContainer:
SRC_QRC_PATH = "src/*Bundle/Resources/qrc/*.qrc"
SRC_RCC_PATH = "src/*Bundle/Resources/qrc/*.rcc"
VENDOR_QRC_PATH = "vendor/*Bundle/Resources/qrc/*.qrc"
VENDOR_RCC_PATH = "vendor/*Bundle/Resources/qrc/*.rcc"
def __init__(self):
self.__pyqt_application = QApplication(sys.argv)
self.__pyqt_resources = QResource()
self.set_rcc_files()
@property
def application(self):
return self.__pyqt_application
@application.setter
def application(self, new_app: QApplication):
self.__pyqt_application = new_app
def set_rcc_files(self):
qrc_files = glob.glob(os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), self.SRC_QRC_PATH))
qrc_files += glob.glob(os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), self.VENDOR_QRC_PATH))
for qrc in qrc_files:
subprocess.call(["rcc", "-binary", qrc, "-o", qrc[:-3] + "rcc"])
rcc_files = glob.glob(os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), self.SRC_RCC_PATH))
rcc_files += glob.glob(os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), self.VENDOR_RCC_PATH))
for rcc in rcc_files:
self.__pyqt_resources.registerResource(rcc)
如您所见,我使用rcc
command,而不是pyrcc5
最后,这是我的.qrc
文件:
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource>
<file alias="jquery.js">../js/jquery.js</file>
<file alias="app.js">../js/app.js</file>
<file alias="qwebchannel.js">../js/qwebchannel.js</file>
<file alias="materialize.js">../css/materialize/js/materialize.js</file>
<file alias="materialize.css">../css/materialize/css/materialize.css</file>
</qresource>
</RCC>
我知道 javascript 代码和 python 代码可以有很多改进和优化。但它是这样工作的!
谢谢,希望我也能帮助别人。
【讨论】:
以上是关于如何使用 QWebEngineView 和 qtwebchannel.js 逐步加载 javascript?的主要内容,如果未能解决你的问题,请参考以下文章
使用QWebEngineView显示html时如何获取verticalScrollBar的最大值和当前值