PyScript加载外部Python脚本时函数无法被HTML按钮识别(Django环境)
问题描述
我编写了一个包含绘图逻辑与current_time函数的Python脚本:
import matplotlib.pyplot as plt import numpy as np from pyscript import Element import datetime x = np.random.randn(1000) y = np.random.randn(1000) fig, ax = plt.subplots() ax.scatter(x, y) display(fig, target="plot") def current_time(): now = datetime.datetime.now() # 获取指定id的段落元素 paragraph = Element("current-time") # 向段落写入当前时间 paragraph.write(now.strftime("%Y-%m-%d %H:%M:%S"))
在Django环境中,我通过<py-script src="{% static 'py/script.py' %}"></py-script>将该脚本加载到HTML中,脚本的绘图功能可正常执行,但HTML按钮的py-click="current_time()"触发时会提示NameError: name 'current_time' is not defined;若将current_time函数直接嵌入HTML的<py-script>标签内,按钮则能正常调用函数。
外部脚本加载场景(报错)
{% extends "proj/base.html" %} {% load static %} {% block head %} <link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css"/> <script defer src="https://pyscript.net/latest/pyscript.js"></script> <py-config type="toml"> packages = ["numpy", "matplotlib"] </py-config> {% endblock head %} {% block content %} <div id="plot"></div> <button py-click="current_time()" id="get-time" class="py-button">获取当前时间</button> <p id="current-time"></p> {% endblock content %} {% block script %} <py-script src="{% static 'py/script.py' %}"></py-script> {% endblock script %}
函数嵌入HTML标签场景(正常工作)
{% extends "proj/base.html" %} {% load static %} {% block head %} <link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css"/> <script defer src="https://pyscript.net/latest/pyscript.js"></script> <py-config type="toml"> packages = ["numpy", "matplotlib"] </py-config> {% endblock head %} {% block content %} <div id="plot"></div> <button py-click="current_time()" id="get-time" class="py-button">获取当前时间</button> <p id="current-time"></p> <py-script> import datetime def current_time(): now = datetime.datetime.now() # 获取指定id的段落元素 paragraph = Element("current-time") # 向段落写入当前时间 paragraph.write(now.strftime("%Y-%m-%d %H:%M:%S")) </py-script> {% endblock content %} {% block script %} <py-script src="{% static 'py/script.py' %}"></py-script> {% endblock script %}
基础模板代码
{% load static %} <!DOCTYPE html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>{{ title }}</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous"> <!-- 自定义样式 --> <link href="{% static 'css/proj.css' %}" rel="stylesheet"> <link href="{% static 'css/forms.css' %}" rel="stylesheet"> <!-- 头部扩展块 --> {% block head %}{% endblock %} </head> <body> <main class="d-flex flex-nowrap"> {% include 'proj/navbar.html' %} <div class="b-example-divider b-example-vr"></div> <div class="content"> {% block content %}{% endblock %} </div> </main> <!-- 脚本扩展块 --> {% block script %}{% endblock %} <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js" integrity="sha384-oBqDVmMz9ATKxIep9tiCxS/Z9fNfEXiDAYTujMAeBAsjFuCZSmKbSSUnQlmh/jp3" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.min.js" integrity="sha384-IDwe1+LCz02ROU9k972gdyvl+AESN10+x7tBKgc9I5HFtuNz0wWnPclzo6p9vxnk" crossorigin="anonymous"></script> </body> </html>
原因分析
这是PyScript的作用域机制导致的:
- 内联
<py-script>标签中定义的函数,默认会被加入全局PyScript命名空间,HTML元素的py-click等属性可以直接访问这个命名空间的内容。 - 通过
src加载的外部脚本,默认运行在独立的模块作用域中,内部定义的函数不会自动暴露到全局命名空间,因此HTML按钮无法找到current_time函数。
解决方案
要让外部脚本的函数能被py-click调用,需要手动将函数暴露到全局命名空间,有两种常用方法:
方法1:挂载函数到pyscript.window
修改外部脚本script.py,将current_time函数挂载到全局window对象:
import matplotlib.pyplot as plt import numpy as np from pyscript import Element, window import datetime # 绘图逻辑保持不变 x = np.random.randn(1000) y = np.random.randn(1000) fig, ax = plt.subplots() ax.scatter(x, y) display(fig, target="plot") def current_time(): now = datetime.datetime.now() paragraph = Element("current-time") paragraph.write(now.strftime("%Y-%m-%d %H:%M:%S")) # 将函数挂载到全局window对象 window.current_time = current_time
方法2:使用@pyscript.expose装饰器
PyScript提供的expose装饰器可以直接将函数暴露到全局命名空间:
import matplotlib.pyplot as plt import numpy as np from pyscript import Element, expose import datetime # 绘图逻辑保持不变 x = np.random.randn(1000) y = np.random.randn(1000) fig, ax = plt.subplots() ax.scatter(x, y) display(fig, target="plot") # 用装饰器暴露函数 @expose def current_time(): now = datetime.datetime.now() paragraph = Element("current-time") paragraph.write(now.strftime("%Y-%m-%d %H:%M:%S"))
修改完成后,外部脚本中的current_time函数就能被py-click="current_time()"正常调用了。
内容的提问来源于stack exchange,提问作者Alex44
相关产品推荐
相关产品推荐

