1. 使用积木构建
  2. 区块中的状态

块中的状态

我们讨论了State in Interfaces,本指南将介绍Blocks中的状态,其工作原理基本相同。

全局状态

Blocks中的全局状态与Interface中的工作方式相同。在函数调用之外创建的任何变量都是所有用户之间共享的引用。

会话状态

Gradio 支持会话 状态,在 Blocks 应用中,数据可以在页面会话中的多次提交之间持久化。再次强调,会话数据不会在模型的不同用户之间共享。要在会话状态中存储数据,你需要做三件事:

  1. 创建一个gr.State()对象。如果这个有状态对象有默认值,将其传递给构造函数。
  2. 在事件监听器中,根据需要将State对象作为输入和输出。
  3. 在事件监听器函数中,将变量添加到输入参数和返回值中。

让我们来看一个简单的例子。下面我们有一个简单的结账应用程序,您可以在其中将商品添加到购物车。您还可以看到购物车的大小。

import gradio as gr

with gr.Blocks() as demo:
    cart = gr.State([])
    items_to_add = gr.CheckboxGroup(["Cereal", "Milk", "Orange Juice", "Water"])

    def add_items(new_items, previous_cart):
        cart = previous_cart + new_items
        return cart

    gr.Button("Add Items").click(add_items, [items_to_add, cart], cart)

    cart_size = gr.Number(label="Cart Size")
    cart.change(lambda cart: len(cart), cart, cart_size)

demo.launch()

注意我们如何使用状态来实现这一点:

  1. 我们将购物车项目存储在gr.State()对象中,这里初始化为一个空列表。
  2. 当向购物车添加商品时,事件监听器将购物车同时作为输入和输出 - 它返回包含所有商品的更新后的购物车。
  3. 我们可以为购物车附加一个.change监听器,该监听器也使用状态变量作为输入。

你可以将gr.State视为一个可以存储任何类型值的不可见组件。在这里,cart在前端不可见,但用于计算。

状态变量的.change监听器在任何事件监听器更改状态变量的值后触发。如果状态变量包含一个序列(如列表、集合或字典),则如果其中的任何元素发生变化,都会触发更改。如果它包含一个对象或原始值,则如果值的哈希发生变化,就会触发更改。因此,如果您定义了一个自定义类并创建了一个gr.State变量,该变量是该类的一个实例,请确保该类包含一个合理的__hash__实现。

当用户刷新页面时,会话状态变量的值会被清除。该值在用户关闭标签页后会在应用后端存储60分钟(这可以通过gr.Blocks中的delete_cache参数进行配置)。

了解更多关于State的信息,请参阅文档

本地状态

Gradio 还支持 本地状态,即使页面刷新或关闭后,数据仍会保留在浏览器的 localStorage 中。这对于存储用户偏好、设置、API 密钥或其他应在会话之间保留的数据非常有用。要使用本地状态:

  1. 创建一个gr.BrowserState()对象。您可以选择提供一个初始默认值和一个键来标识浏览器localStorage中的数据。
  2. 像常规的gr.State组件一样在事件监听器中作为输入和输出使用。

这是一个简单的示例,用于在会话之间保存用户的用户名和密码:

import random
import string
import gradio as gr
import time
with gr.Blocks() as demo:
    gr.Markdown("Your Username and Password will get saved in the browser's local storage. "
                "If you refresh the page, the values will be retained.")
    username = gr.Textbox(label="Username")
    password = gr.Textbox(label="Password", type="password")
    btn = gr.Button("Generate Randomly")
    local_storage = gr.BrowserState(["", ""])
    saved_message = gr.Markdown("✅ Saved to local storage", visible=False)

    @btn.click(outputs=[username, password])
    def generate_randomly():
        u = "".join(random.choices(string.ascii_letters + string.digits, k=10))
        p = "".join(random.choices(string.ascii_letters + string.digits, k=10))
        return u, p

    @demo.load(inputs=[local_storage], outputs=[username, password])
    def load_from_local_storage(saved_values):
        print("loading from local storage", saved_values)
        return saved_values[0], saved_values[1]

    @gr.on([username.change, password.change], inputs=[username, password], outputs=[local_storage])
    def save_to_local_storage(username, password):
        return [username, password]

    @gr.on(local_storage.change, outputs=[saved_message])
    def show_saved_message():
        timestamp = time.strftime("%I:%M:%S %p")
        return gr.Markdown(
            f"✅ Saved to local storage at {timestamp}",
            visible=True
        )

demo.launch()