有没有办法从 pyodide runPython 函数内部更改 html textContent?

问题描述 投票:0回答:1

例如,我的 .html 文档中的 pyodide 脚本中有一个 for 循环。有没有办法直接从 pyodide for 循环更改 div 的 textContent。

在下面的例子中,只有 for 循环的最后一个值(在我的例子中是 99)然后被发送到“myDiv”。 甚至可以直接从 pyodide 脚本更改 textContent 吗?

<head>
    <script src="https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.js"></script>
</head>
<body>
    <div id="myDiv">Text that needs to change</div>
    <script>
        async function main() {
            let pyodide = await loadPyodide();
            return pyodide;
        }

        let pyodideReadyPromise = main();
        async function pythonChange() {
            let pyodide = await pyodideReadyPromise;
            pyodide.runPython(`
                from js import document
          
                print("started")
          
                for i in range(100):
                    print(i)
                    document.getElementById("myDiv").textContent = i
              
                print("finished")
                `)
        }
        pythonChange();
    </script>
</body>
javascript python html for-loop pyodide
1个回答
0
投票

问题不在于 textContent 没有被改变;问题是屏幕没有机会更新以实际显示更改。解决方案是使用协程。

确认确实发生了变化

要观察 textContent 确实在变化,我们可以在脚本标签的最开头添加一个小的 Mutation Observer。这将记录到浏览器开发控制台对观察到的DOM节点的任何更改:

function callback(mutationList, observer){
    mutationList.forEach(mutation => console.log(record.target))
}
const MO = new MutationObserver(callback)
MO.observe(document.getElementById("myDiv"), {attributes: true })

使用此添加的代码,控制台将填充

started
0
1
、...、
98
99
finished
。所以 textContent 实际上是在变化的,这很好。

将事物减慢到人类的速度

您可能认为代码只是执行得太快,您的眼睛看不到数字的变化,但这也不是正在发生的事情。让我们通过修改 for 循环来减慢速度:

for i in range(100):
    print(i)
    document.getElementById("myDiv").textContent = i
    for j in range(1_000_000):
        _ = 1

现在你的循环在前进到下一个数字之前必须“做一些无用的工作”。 (您可能需要将

1_000_000
更改为更大或更小的数字,具体取决于您的系统。)如果您打开开发者控制台,您会看到数字 0 到 99 以更缓慢的速度出现。但是在 Python 代码完成之前,页面上的文本不会更新。那给了什么?

真正的问题

问题在于,虽然对 DOM 的更新是同步(即在 DOM 更新完成之前不会执行进一步的代码),对屏幕的更新是异步的。更重要的是,对

runPython()
的整个调用是同步的,因此在
runPython
终止之前不会发生对屏幕的更新。本质上,对
runPython
的调用是一个阻塞调用,页面上不会发生任何其他事情——屏幕更新和重绘、其他 JavaScript 调用等——直到
runPython
返回。

这篇博文对同步代码和屏幕上可见变化之间的交互给出了很好的高级解释。

解决方案

那么,如果在我们的同步代码调用终止之前屏幕无法更新,我们该怎么办?让我们的代码异步!通过将我们的代码变成一个协程,它偶尔会退回到浏览器的事件循环来做一些工作(即更新屏幕),我们可以在更新发生时看到它们。

Pyodide 以 runPythonAsync 函数的形式为此提供了一个漂亮的实用程序,它允许您编写异步代码,而无需求助于将代码包装到协程中。这是 此功能的描述及其用途 从它在 PyScript 中使用开始。

这是一个最终代码示例,它将替换原始示例中对

pyodide.runPython
的整个调用。我已将减速代码留在原处,以便结果可见,但在生产中不需要它。

pyodide.runPythonAsync(`
    from js import document
    from asyncio import sleep

    print("started")

    for i in range(100):
        print(i)
        document.getElementById("myDiv").textContent = i
        await sleep(0.01)
        for j in range(1_000_000):
            _ = 1
    
    print("finished")
    `)
© www.soinside.com 2019 - 2024. All rights reserved.