You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Shiny动态添加标签页时如何保留输入输出状态?

解决Python Shiny动态标签页状态重置问题

你的问题出在每次点击"+"时,整个导航栏会被重新渲染,旧标签的组件被销毁后重建,同时模块服务器也会被重复初始化,导致输入状态全部丢失。另外,所有标签的输入框ID都是test_text,多个标签共存时会引发ID冲突,这也会影响状态保存。

下面是修复后的代码:

from shiny import App, Inputs, Outputs, Session, module, reactive, render, ui

# 模块UI:确保输入ID唯一,和panelNum绑定
@module.ui
def textbox_ui(panelNum):
    return ui.nav_panel(
        f"Tab {panelNum}",
        ui.input_text_area(id=f"test_text_{panelNum}",  # 修改ID为唯一值
                           label="Enter some text"),
        ui.output_text(f"display_text_{panelNum}"),  # 用output_text更合适,因为是渲染文本
        value=f"Tab_{panelNum}"
    )

# 模块服务器:对应唯一的输入ID
@module.server
def textbox_server(input, output, session, panelNum):
    @output(id=f"display_text_{panelNum}")
    @render.text
    def return_text():
        return input[f"test_text_{panelNum}"]()  # 读取对应ID的输入

# 应用UI
app_ui = ui.page_fluid(
    ui.tags.head(
        ui.tags.style(
            """
            body {
                height: 100vh;
                overflow-y: auto !important;
            }
            """
        )
    ),
    ui.h2('Test App', align='center', fillable=True),
    ui.navset_tab(
        ui.nav_panel("Home",
                     ui.card(
                         ui.card_header("Overview"),
                         ui.p("An example of the outputs clearing")
                     ),
                     value="panel0"
        ),
        ui.nav_panel("+", value="add_tab"),  # 给"+"标签设置明确的value
        id="shiny_tabs"
    ),
    title="Test App"
)

# 应用服务器
def server(input, output, session):
    # 存储已创建的标签数量,以及模块服务器实例(避免重复初始化)
    tab_count = reactive.value(0)
    module_instances = reactive.list()

    # 监听"+"标签的点击事件
    @reactive.effect
    @reactive.event(input.shiny_tabs)
    def add_tabs():
        if input.shiny_tabs() == "add_tab":
            tab_count.set(tab_count.get() + 1)
            new_tab_num = tab_count.get()
            # 仅初始化一次新标签的模块服务器
            module_instances.append(
                textbox_server(f"tab_{new_tab_num}", panelNum=new_tab_num)
            )
            # 动态插入新标签到导航栏(不是重建整个导航)
            ui.insert_nav(
                id="shiny_tabs",
                nav=textbox_ui(f"tab_{new_tab_num}", panelNum=new_tab_num),
                target="add_tab",  # 插入到"+"标签之前
                position="before"
            )
            # 切换到新创建的标签
            ui.update_navs("shiny_tabs", selected=f"Tab_{new_tab_num}")

app = App(app_ui, server)

关键改动说明:

  • 唯一ID设置:把输入框ID改成test_text_{panelNum},避免多个标签的输入组件ID冲突,确保每个输入的状态独立存储。
  • 动态插入标签:不再每次重建整个导航栏,而是用ui.insert_nav()在点击"+"时动态插入新标签,保留原有标签的组件和状态。
  • 模块服务器仅初始化一次:用reactive.list()存储已创建的模块实例,只在新增标签时初始化对应的服务器,避免重复创建导致状态丢失。
  • 明确"+"标签的value:给"+"标签设置value="add_tab",更清晰地判断点击事件。

这样修改后,新增标签时原有标签的输入和输出状态就会被保留下来。

内容的提问来源于stack exchange,提问作者Devdan Kim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 09:11:06