Page2 - Multiply
2026/9/13 7:40:02 网站建设 项目流程

Page2 - Multiply

【免费下载链接】taipyTurns Data and AI algorithms into production-ready web applications in no time.项目地址: https://gitcode.com/GitHub_Trending/ta/taipy

Operand 1: <|{operand_1}|slider|id=s1|>

Operand 2: <|{operand_2}|slider|id=s2|>

Operand 1 * Operand 2 = <|{operand_1 * operand_2}|id=v1|>

这是一份完整的 Taipy Markdown 页面模板,体积虽小,却浓缩了 Taipy GUI 的三大核心机制: 1. **控件声明**:通过 `<|...|>` 语法声明了两个滑块控件(`slider`),分别绑定到变量 `operand_1` 与 `operand_2`,并各自带有一个 DOM 标识(`id=s1` / `id=s2`),供前端脚本与测试定位; 2. **表达式绑定**:页面底部用 `<|{operand_1 * operand_2}|id=v1|>` 实时渲染两个操作数的乘积,说明控件属性不仅能绑定单个变量,还能直接绑定 Python 表达式; 3. **作用域语义**:`operand_1` 与 `operand_2` 一个来自主模块全局作用域、一个来自页面类实例作用域,二者的绑定差异正是本测试目录(`assets2_class_scopes`)要验证的核心行为。 ## 逐行拆解:Taipy Markdown 控件语法与表达式求值 ### `<|{operand_1}|slider|id=s1|>`:滑块控件的声明方式 `<|...|>` 是 Taipy Markdown 渲染器识别控件的专用语法,其通用格式为: ```markdown <|{绑定表达式}|控件类型|属性名=属性值|...|>

以第一行为例:

  • {operand_1}:控件的"值"属性,大括号表示这是一个可双向绑定的变量——前端滑动滑块会回写该变量,后端变量变化也会推送回前端;
  • slider:控件类型标识,声明这是一个滑块控件;
  • id=s1:为控件指定一个稳定的 DOM 标识。该标识直接映射到页面 HTML 元素的id属性,这不仅是前端调试、自动化测试的锚点,也是 Taipy 内部识别与更新该控件的依据。

在 e2e 测试中可以看到该id的实际用途:test_class_scopes_binding_global.py 通过page.wait_for_selector("#s1")page.fill("#s1 input", "15")以及page.input_value("#s1 input")来等待、写入并断言滑块的值,#v1则用于读取乘积结果文本。

<|{operand_1 * operand_2}|id=v1|>:属性值支持表达式求值

第三行没有控件类型,直接以表达式作为默认内容。Taipy 会在前端(浏览器端)对{...}内的表达式进行求值并随变量变化实时刷新,因此当你拖动任意一个滑块时,operand_1 * operand_2的结果会立即更新,无需任何 Python 回调。这种"表达式即显示"的能力来自 Taipy 渲染层对属性值的求值管线,例如在 taipy/gui/_renderers/builder.py 中可以看到控件属性通过gui._evaluate_expr(...)解析表达式、并通过_getscopeattr(...)从作用域取值的实现路径。可以推断:渲染器在生成页面时将{...}表达式解析为前端可执行的表达式,同时在后端维护变量与控件的绑定关系。

从 Markdown 到类作用域页面:Page子类与create_page

page2.md本身只是模板文件,它由同目录下的类模块 page2.py 加载:

from taipy.gui import Markdown, Page class Page2(Page): def __init__(self) -> None: self.operand_2 = 0 super().__init__() def create_page(self): return Markdown("page2.md")

这揭示了一个关键模式:页面级变量的作用域由类的实例属性定义

  • 构造方法中self.operand_2 = 0operand_2定义为该页面实例的成员变量(初始值 0),它只属于这个页面对象,与其它页面互不干扰;
  • create_page()Page基类定义的钩子方法,返回页面内容。基类 taipy/gui/page.py 的文档明确指出:子类必须重载create_page()并返回页面内容,返回值可以是字符串——此时 Taipy 会将其当作 Markdown 内容解析。此处返回Markdown("page2.md"),即读取同一目录下的模板文件生成页面渲染器;
  • super().__init__()触发基类构造逻辑:基类会调用create_page()获取渲染器,随后通过dict(vars(self))inspect.getmembers(cls)收集页面实例的属性与方法,存入_class_locals(见 taipy/gui/page.py),这些局部变量构成了该页面的独立作用域。

同目录的 page1.py 展示了同类模式的加法页,并额外带有一个回调方法:

class Page1(Page): def __init__(self) -> None: self.operand_2 = 0 super().__init__() def create_page(self): return Markdown("page1.md") def reset(state): state.operand_2 = 0

其模板 page1.md 中对应地多了<|button|on_action=reset|id=btn_reset|>按钮——点击后调用reset回调,通过state.operand_2 = 0本页面operand_2归零。这个细节再次印证了operand_2是页面私有状态的定位。

全局变量与页面作用域变量的绑定语义

理解了模板与类模块之后,再看测试 test_class_scopes_binding_global.py 是如何验证绑定行为的。测试的关键装配代码如下:

gui._set_frame(inspect.currentframe()) operand_1 = 0 # noqa: F841 gui.add_page("page1", Page1()) gui.add_page("page2", Page2()) helpers.run_e2e(gui)

这里刻意构造了两种变量来源:

  • operand_1定义在测试函数(主模块)的局部作用域中,属于"全局绑定"变量,被page1page2两个页面共同引用、共享同一份值;
  • operand_2定义在Page1/Page2各自的实例上,属于"类作用域"变量,每个页面维护自己独立的副本。

Gui.add_page(name, page)(taipy/gui/gui.py)在注册页面时会调用_add_page_context更新局部变量上下文:对类模块页面,通过page._get_locals()取得_class_locals作为该页面的作用域来源;而对普通模块页面,则会self.__var_dir.add_frame(page._frame)记录其调用栈帧。这意味着:类页面变量的生命周期与实例绑定,普通(函数/模块级)变量的生命周期与主模块绑定

端到端验证:跨页面导航下的数据流推演

Playwright 测试模拟了真实用户的操作序列,是理解绑定语义的最佳教材。逐步推演如下:

  1. 进入 page1:初始断言三个控件值均为0/0/0。把s1填为 15,v1变为15;再把s2填为 20,v1变为35(15 + 20)。此时全局operand_1 = 15,page1 的operand_2 = 20
  2. 导航到 page2:断言滑块s1显示15(全局变量跨页面共享、值被保留),s2显示0(page2 的operand_2是全新实例,从初始值 0 开始),v1 = 15 * 0 = 0。把s2填为 5,v1变为75;把s1填为 17,v1变为85。此时全局operand_1 = 17,page2 的operand_2 = 5
  3. 返回 page1:断言v1 = 37(17 + 20)。关键点在于 page1 的operand_2依然是此前设置的 20——页面实例在导航往返后状态被保留,未被 page2 的 5 覆盖;
  4. 点击 Reset 按钮:触发reset回调把 page1 的operand_2置 0,最终v1 = 17

这一组断言完整刻画了两种作用域的生命周期差异:

行为场景operand_1(全局/主模块)operand_2(类实例)
在 page1 修改影响所有页面仅影响 page1
导航到 page2值保留(15/17)从初始值 0 重新开始
返回 page1值保留(17)保留修改前的值(20)
点击 reset不受影响page1 重置为 0

测试中大量使用的page.wait_for_function("document.querySelector('#v1').innerText !== '0'")则验证了前端表达式求值的实时性:变量经滑块写入后,乘积表达式在浏览器端立即重算并刷新 DOM,无需刷新页面或调用 Python。

把类作用域页面模式用进你自己的应用

综合上述分析,你可以直接用以下最小可运行骨架复刻这套模式(注意将page2.md模板与Page2类放在同一目录):

# app.py from taipy.gui import Gui from pages.page2 import Page2 # 你的类模块 gui = Gui() gui.add_page("page2", Page2()) # 注册类作用域页面 gui.run()

模板文件pages/page2.md保持与上文一致的滑块 + 表达式写法即可。使用中需要留意的要点:

  • 变量归属即作用域:希望跨页面共享的数据(如用户信息、全局配置)放在主模块/全局变量中;希望页面私有的状态(如表单草稿、分页偏移)定义为Page子类的实例属性;
  • 页面名约束add_page的页面名不能以/开头、只能包含字母、数字、-_/,且不能与已有路由重复(见 taipy/gui/gui.py 的校验逻辑);
  • id属性的价值:为控件指定稳定的id既方便前端自动化测试锚定元素,也让表达式求值与变量回写有明确的定位依据;
  • 初始化顺序:实例属性必须在super().__init__()之前赋值(Page2.__init__self.operand_2 = 0位于super().__init__()之前),因为基类构造时会通过vars(self)快照实例属性作为页面作用域。

如果你想进一步验证这一行为,可以直接运行仓库中的端到端测试:

pytest tests/gui/e2e/page_scopes/test_class_scopes_binding_global.py -m teste2e

【免费下载链接】taipyTurns Data and AI algorithms into production-ready web applications in no time.项目地址: https://gitcode.com/GitHub_Trending/ta/taipy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询