【Gradio】Building With Blocks 块和事件监听器

c7a9edde05ccf28386950ca8318b3e44.png

我们在快速入门中简要描述了 Blocks 类,作为构建自定义演示的一种方式。让我们更深入地研究。

 Blocks 结构 

看看下面的演示。

import gradio as gr  # 导入gradio库,用于创建交云式Web应用界面


# 定义一个函数,接受一个名字作为输入,返回一个问候语
def greet(name):
    return "Hello " + name + "!"  # 拼接问候语


# 使用‘with’语法和Blocks API构造Gradio界面
with gr.Blocks() as demo:
    name = gr.Textbox(label="Name")  # 创建一个文本框,用于输入名字
    output = gr.Textbox(label="Output Box")  # 创建一个文本框,用于显示问候语输出
    greet_btn = gr.Button("Greet")  # 创建一个按钮,点击后将触发问候语函数
    # 为按钮设置点击事件,绑定greet函数,指定输入为name文本框,输出到output文本框
    greet_btn.click(fn=greet, inputs=name, outputs=output, api_name="greet")


demo.launch()  # 启动Gradio界面

dd9f631f5d844d5a88cc7f58b3fd8ccc.png

代码使用了Gradio的Blocks API来构建一个简单的交互式Web页面,页面包含一个输入框、一个输出框和一个按钮。用户在输入框中输入名字,点击按钮后,页面会在输出框中显示相应的问候语:"Hello [输入的名字]!"。这是通过绑定按钮的点击事件到一个自定义的greet函数实现的,greet函数接收输入框中的名字作为参数,并返回构造的问候语。最后,通过调用.launch()方法来启动和展示Gradio界面。这个界面可以作为一个交互式的问候工具。

  • 首先,注意 with gr.Blocks() as demo: 条款。Blocks 应用程序代码将包含在此条款中。

  • 接下来是组件。这些是在 Interface 中使用的相同组件。但是,组件不是被传递给某个构造函数,而是在 with 子句中创建块时自动添加的。

  • 最后是 click() 事件监听器。事件监听器定义了应用程序内的数据流。在上面的例子中,监听器将两个文本框连接在一起。文本框 name 作为输入,文本框 output 作为输出到 greet 方法。当点击按钮 greet_btn 时,会触发这个数据流。就像一个接口,事件监听器可以有多个输入或输出。

您还可以使用装饰器附加事件监听器 - 跳过 fn 参数并直接分配 inputs 和 outputs 

import gradio as gr  # 导入gradio模块,用于构建交互式的Web应用


# 使用gr.Blocks创建一个布局,这允许我们更灵活地设计页面
with gr.Blocks() as demo:
    name = gr.Textbox(label="Name")  # 创建一个标签为"Name"的文本框用于用户输入名字
    output = gr.Textbox(label="Output Box")  # 创建一个标签为"Output Box"的文本框用于显示输出
    greet_btn = gr.Button("Greet")  # 创建一个文本为"Greet"的按钮


    # 定义点击"greet_btn"按钮时触发的行为
    @greet_btn.click(inputs=name, outputs=output) 
    def greet(name):  # 定义一个问候函数,输入参数是name
        return "Hello " + name + "!"  # 拼接问候语并返回


demo.launch()  # 启动demo,展现定义好的交云界面

事件监听和交互性 

在上面的例子中,您会注意到您可以编辑文本框 name ,但不能编辑文本框 output 。这是因为任何作为事件监听器输入的组件都会变得可交互。然而,由于文本框 output 仅作为输出,Gradio 决定不应该使其可交互。您可以覆盖默认行为,并直接使用布尔 interactive 关键字参数配置组件的交互性。

output = gr.Textbox(label="Output", interactive=True)

注意:如果 Gradio 组件既不是输入也不是输出会怎样?如果一个组件被构造了一个默认值,那么它被假定为显示内容并且被渲染为非交互式。否则,它被渲染为交互式。同样,这种行为可以通过为 interactive 参数指定一个值来覆盖。

事件监听器类型 

看看下面的演示:

import gradio as gr  # 导入gradio库


# 定义一个欢迎函数,接收用户输入的名字
def welcome(name):
    return f"Welcome to Gradio, {name}!"  # 返回带有用户名字的欢迎信息


# 使用gr.Blocks创建交互界面的布局
with gr.Blocks() as demo:
    gr.Markdown(  # 创建一个Markdown组件显示标题和指示信息
    """
    # Hello World!
    Start typing below to see the output.
    """)
    inp = gr.Textbox(placeholder="What is your name?")  # 创建一个文本输入框,提示用户输入名字
    out = gr.Textbox()  # 创建一个文本框用于显示输出信息
    inp.change(welcome, inp, out)  # 绑定inp文本框的内容改变事件到欢迎函数


demo.launch()  # 启动应用,显示交互界面

此段代码采用Gradio的Blocks API构建了一个简单的Web应用。它包含一个Markdown组件来显示一段欢迎信息和说明文字,一个输入框inp供用户输入名字,一个输出框out用来显示根据用户输入生成的欢迎信息。当用户在inp文本框中输入名字时,绑定到该文本框的change事件被触发,执行welcome函数。该函数根据用户输入的名字返回一条欢迎信息,然后这条信息自动出现在out文本框中。

0cad0de80b637ae9ead1c1dba7e0d21c.png

不是由点击触发,而是由在文本框 inp 中输入触发 welcome 函数。这是由于 change() 事件监听器。不同的组件支持不同的事件监听器。例如, Video 组件支持 play() 事件监听器,当用户按下播放时触发。查看每个组件的事件监听器文档。

多重数据流 

Blocks 应用程序不像接口那样仅限于单一数据流。请看下面的演示:

import gradio as gr  # 导入gradio库


# 定义一个增加函数,将输入数值加一
def increase(num):
    return num + 1


# 使用gr.Blocks来创建布局
with gr.Blocks() as demo:
    a = gr.Number(label="a")  # 创建一个数字输入框,标签为"a"
    b = gr.Number(label="b")  # 创建另一个数字输入框,标签为"b"
    atob = gr.Button("a > b")  # 创建一个按钮,表示将"a"的值增加后显示在"b"
    btoa = gr.Button("b > a")  # 创建另一个按钮,表示将"b"的值增加后显示在"a"
    
    # 当点击"atob"按钮时,调用increase函数,输入是a的值,输出是b的值
    atob.click(increase, a, b)
    # 当点击"btoa"按钮时,调用increase函数,输入是b的值,输出是a的值
    btoa.click(increase, b, a)


demo.launch()  # 启动应用,显示界面

代码实现了一个简单的数字增加应用,用户可以在其中输入两个数字。通过点击两个不同的按钮:"a > b"和"b > a",用户可以实现将输入框ab的数值分别加一后显示在另一个输入框中。increase函数用于实现数值的增加操作。点击按钮时,依据按钮的不同,increase函数将作用于不同的输入,把结果输出到相对应的另一个输入框。这样的设计允许用户通过界面直觉地交互,通过按钮即可实现简单的数值运算和数据的传递。

请注意, num1 可以作为 num2 的输入,反之亦然!随着您的应用程序变得更加复杂,您将有许多数据流连接各种组件。

这里有一个“多步骤”的演示示例,其中一个模型(语音转文本模型)的输出被输入到下一个模型(情感分类器)。

from transformers import pipeline  # 导入transformers库,用于加载预训练的模型


import gradio as gr  # 导入gradio库


# 加载自动语音识别(ASR)模型
asr = pipeline("automatic-speech-recognition", "facebook/wav2vec2-base-960h")
# 加载文本分类模型
classifier = pipeline("text-classification")


# 定义一个将语音转换为文本的函数
def speech_to_text(speech):
    text = asr(speech)["text"]  # 使用ASR模型将语音文件转换为文本
    return text


# 定义一个将文本进行情感分类的函数
def text_to_sentiment(text):
    return classifier(text)[0]["label"]  # 使用文本分类模型对文本进行情感分析,返回标签


demo = gr.Blocks()  # 创建Gradio交互界面


with demo:
    audio_file = gr.Audio(type="filepath")  # 创建一个音频输入组件,用户上传音频文件
    text = gr.Textbox()  # 创建一个文本框组件,用于显示识别出的文本
    label = gr.Label()  # 创建一个标签组件,用于显示文本情感分类的结果


    b1 = gr.Button("Recognize Speech")  # 创建一个按钮,用于触发语音识别
    b2 = gr.Button("Classify Sentiment")  # 创建一个按钮,用于触发文本情感分类


    # 绑定按钮点击事件到相应的函数,设置输入输出组件
    b1.click(speech_to_text, inputs=audio_file, outputs=text)
    b2.click(text_to_sentiment, inputs=text, outputs=label)


demo.launch()  # 启动应用,显示界面

0f1182aaed6ad6c30b8542f309b0bc0e.png

代码结合了Hugging Face的transformers和Gradio库,实现了一个能够进行自动语音识别和文本情感分类的Web应用。首先,通过transformers库加载了wav2vec2-base-960h模型进行自动语音识别,以及加载了一个文本分类模型进行情感分析。定义了两个函数:speech_to_text将上传的音频文件转换成文本,text_to_sentiment则判定这段文本的情感倾向。Gradio交互界面包括一个音频上传组件用于用户上传语音文件,一个文本显示组件用于展示语音识别结果,和一个标签组件用于显示文本情感分析结果。两个按钮分别触发语音识别和文本情感分类的过程。这样,用户可以通过一个简单的界面上传语音文件,获取语音转文本的结果以及对这段文本情绪的分类。

函数输入列表与字典

您迄今为止看到的事件监听器有一个单独的输入组件。如果您希望有多个输入组件将数据传递给函数,函数可以接受输入组件值有两种选择: 

  1. 作为参数列表,或

  2. 作为由组件键控的值的单个字典

让我们看一个每个的例子: 

import gradio as gr  # 导入gradio库


# 使用gr.Blocks创建应用布局
with gr.Blocks() as demo:
    a = gr.Number(label="a")  # 创建一个数字输入组件,标签为"a"
    b = gr.Number(label="b")  # 创建一个数字输入组件,标签为"b"
    with gr.Row():  # 将下面的组件放置在同一行中
        add_btn = gr.Button("Add")  # 创建一个按钮,用于执行加法操作
        sub_btn = gr.Button("Subtract")  # 创建一个按钮,用于执行减法操作
    c = gr.Number(label="sum")  # 创建一个数字显示组件,用于显示计算结果,标签为"sum"


    # 定义一个加法函数
    def add(num1, num2):
        return num1 + num2
    # 将add_btn按钮点击事件绑定到add函数
    add_btn.click(add, inputs=[a, b], outputs=c)


    # 定义一个减法函数
    def sub(data):  
        # data 参数是一个字典,包含了所有与按钮交互的组件的值
        # 这里应该使用字典下标访问,而非直接使用data[a] - data[b]
        # 修改如下:
        return data["a"] - data["b"]  # 从data字典中获取a和b的值,并执行减法
    # 将sub_btn按钮点击事件绑定到sub函数,inputs应使用字典形式访问
    sub_btn.click(sub, inputs={"a": a, "b": b}, outputs=c)  # 正确传递参数应该为字典形式,而不是集合


demo.launch()  # 启动应用

15a59c405bd73d5c00ee10e96c9af418.png

代码创建了一个Gradio Web应用,用于执行基本的加法和减法运算。用户可以在两个数字输入框内输入数字,然后通过点击"Add"按钮或"Subtract"按钮执行加法或减法计算。计算结果会在另一个标记为"sum"的数字显示组件中显示。这个小应用通过简单直观的界面,使得用户能够轻松地完成基本的数学运算。在这段代码中,需要注意的是减法函数中的输入方式和怎样从函数中获取指定组件的值,确保数据以正确的形式传入函数中。

函数返回列表与字典 

类似地,您可以通过以下方式为多个输出组件返回值:

  1. 一个值列表,或者

  2. 一个以组件为键的字典

让我们先看一个(1)的例子,在这里我们通过返回两个值来设置两个输出组件的值:

with gr.Blocks() as demo:  # 使用gr.Blocks构建应用布局
    food_box = gr.Number(value=10, label="Food Count")  # 创建一个数字输入组件,初始值为10,标签为"Food Count"
    status_box = gr.Textbox()  # 创建一个文本框组件,用于显示状态信息


    # 定义一个吃食物的函数
    def eat(food):
        if food > 0:  # 如果食物数量大于0
            return food - 1, "full"  # 返回食物数量减1,并显示状态为"full"
        else:  # 如果没有食物
            return 0, "hungry"  # 保持食物数量为0,并显示状态为"hungry"


    # 创建一个按钮,并绑定点击事件
    gr.Button("EAT").click(
        fn=eat,  # 绑定eat函数到按钮的点击事件
        inputs=food_box,  # 设置输入组件为food_box
        outputs=[food_box, status_box]  # 设置输出组件,即食物数量和状态信息将被输出到food_box和status_box
    )

代码创建了一个简单的Gradio应用,模拟了一个吃食物的过程。界面中有一个数字输入组件用来表示食物数量(默认值为10),另外有一个文本框用来显示当前的状态("full"表示饱了,"hungry"表示饿了)。通过点击"EAT"按钮,会触发eat函数。这个函数会检查当前的食物数量,如果食物数量大于0,则食物数量减1,并更新状态为"full";如果食物数量为0,则状态更新为"hungry"。食物数量的更新和状态的更新都会反映在界面上。通过这个简单的交互,用户可以直观地看到每次点击"EAT"按钮后食物数量的变化和当前的状态。

上面,每个返回语句分别返回与 food_box 和 status_box 相对应的两个值。

您可以不返回一个按顺序对应每个输出组件的值列表,而是返回一个字典,键对应输出组件,值为新值。这也允许您跳过更新某些输出组件。

with gr.Blocks() as demo:
    food_box = gr.Number(value=10, label="Food Count")
    status_box = gr.Textbox()
    def eat(food):
        if food > 0:
            return {food_box: food - 1, status_box: "full"}
        else:
            return {status_box: "hungry"}
    gr.Button("EAT").click(
        fn=eat,
        inputs=food_box,
        outputs=[food_box, status_box]
    )

注意,在没有食物的时候,我们只更新 status_box 元素。我们跳过了更新 food_box 组件。

字典返回在事件监听器影响返回时的许多组件,或有条件地影响输出而不影响其他输出时非常有用

请记住,在使用字典返回时,我们仍然需要在事件监听器中指定可能的输出。

更新组件配置 

事件监听函数的返回值通常是相应输出组件的更新值。有时我们也想更新组件的配置,比如可见性。在这种情况下,我们返回一个新的组件,并设置我们想要更改的属性。

import gradio as gr  # 导入gradio库


# 定义一个根据选择变化文本框属性的函数
def change_textbox(choice):
    if choice == "short":  # 如果选择是"short"
        # 返回一个行数为2的文本框,可见
        return gr.Textbox(lines=2, visible=True)
    elif choice == "long":  # 如果选择是"long"
        # 返回一个行数为8的文本框,可见,且默认文本为"Lorem ipsum dolor sit amet"
        return gr.Textbox(lines=8, visible=True, value="Lorem ipsum dolor sit amet")
    else:  # 如果选择是"none"
        # 返回一个不可见的文本框
        return gr.Textbox(visible=False)


with gr.Blocks() as demo:  # 使用gr.Blocks构建应用布局
    radio = gr.Radio(
        # 创建一个单选按钮组件,选项包括"short"、"long"和"none"
        ["short", "long", "none"], label="What kind of essay would you like to write?"
    )
    text = gr.Textbox(lines=2, interactive=True, show_copy_button=True)  # 创建一个初始行数为2的文本框,允许用户交互,显示复制按钮
    # 当用户在单选按钮组件上做出选择时,调用change_textbox函数,并根据返回的结果更新文本框组件
    radio.change(fn=change_textbox, inputs=radio, outputs=text)


demo.launch()  # 启动应用

代码创建了一个Gradio Web应用,其中包含一个单选按钮和一个文本框。用户可以通过单选按钮选择他们想要写的文章种类:短文章("short")、长文章("long")或者不写("none")。选择不同的选项会影响文本框的展示。如果选择"short",文本框将显示2行;选择"long",文本框将扩展到8行并预填充一段文本"Lorem ipsum dolor sit amet";选择"none",文本框将不可见。这个应用通过change_textbox函数动态修改文本框的特性,使其能够响应用户的选择并据此调整界面布局。

a5116403269cd38f12d688404f12dff1.png

看看我们如何通过一个新的 gr.Textbox() 方法来配置文本框本身。 value= 参数仍然可以用来更新值以及组件配置。我们没有设置的任何参数将使用它们之前的值。

示例 

就像使用gr.Interface一样,当您使用gr.Blocks时,您也可以为您的函数添加示例。在这种情况下,实例化一个gr.Examples,就像您实例化任何其他组件一样。gr.Examples的构造函数需要两个必需的参数:

examples:一个嵌套的示例列表,在这个外部列表由示例组成,每个内部列表由对应于每个输入组件的输入组成

inputs:当点击示例时应该填充的组件或组件列表

您也可以设置cache_examples=True类似于gr.Interface,在这种情况下,必须提供两个额外的参数:

outputs:对应于示例输出的组件或组件列表

fn:运行以生成对应于示例的输出的函数

以下是一个示例,展示如何在gr.Blocks应用程序中使用gr.Examples:

import gradio as gr  # 导入gradio库


# 定义一个支持四则运算的计算器函数
def calculator(num1, operation, num2):
    if operation == "add":  # 如果运算为加法
        return num1 + num2
    elif operation == "subtract":  # 如果运算为减法
        return num1 - num2
    elif operation == "multiply":  # 如果运算为乘法
        return num1 * num2
    elif operation == "divide":  # 如果运算为除法
        # 注意,除法可能引发除以0的异常,可以添加错误处理逻辑避免程序崩溃
        if num2 == 0:  # 检查除数是否为0
            return "Error: Division by zero"  # 如果是,返回错误消息
        return num1 / num2


with gr.Blocks() as demo:  # 使用gr.Blocks构建应用布局
    with gr.Row():  # 将下面的内容放置在同一行中
        with gr.Column():  # 创建一个列,用于放置输入组件
            num_1 = gr.Number(value=4)  # 创建一个数字输入组件,初始值为4
            operation = gr.Radio(["add", "subtract", "multiply", "divide"])  # 创建一个单选按钮组件,用于选择操作类型
            num_2 = gr.Number(value=0)  # 创建另一个数字输入组件,初始值为0
            submit_btn = gr.Button(value="Calculate")  # 创建一个提交按钮
        with gr.Column():  # 创建另一个列,用于显示结果
            result = gr.Number()  # 创建一个数字显示组件,用于显示计算结果


    # 当点击Calculate按钮时,调用calculator函数,执行计算,并将结果显示在result组件中
    submit_btn.click(
        calculator, inputs=[num_1, operation, num_2], outputs=[result], api_name=False
    )
    # 创建一组示例输入,方便用户快速测试不同的运算
    examples = gr.Examples(
        examples=[
            [5, "add", 3],  # 示例1
            [4, "divide", 2],  # 示例2
            [-4, "multiply", 2.5],  # 示例3
            [0, "subtract", 1.2],  # 示例4
        ],
        inputs=[num_1, operation, num_2],  # 指定示例的输入对应的组件
    )


if __name__ == "__main__":
    demo.launch(show_api=False)  # 启动应用,不显示API界面

注意:在 Gradio 4.0 或更高版本中,当您点击示例时,不仅输入组件的值会更新为示例值,而且组件的配置也会恢复为您构建组件时的属性。这确保了即使组件的配置已更改,示例也与组件兼容。

连续运行事件

您也可以通过使用事件监听器的 then 方法来连续运行事件。这将在前一个事件运行完成后运行一个事件。这对于需要多步骤更新组件的事件来说非常有用。

例如,在下面的聊天机器人示例中,我们首先立即用用户消息更新聊天机器人,然后在模拟延迟后用计算机响应更新聊天机器人

import gradio as gr  # 导入gradio库
import random  # 导入random库,用于生成随机的响应消息
import time  # 导入time库,用于模拟响应时间


with gr.Blocks() as demo:  # 使用gr.Blocks构建应用布局
    chatbot = gr.Chatbot()  # 创建一个chatbot组件,用于显示聊天信息
    msg = gr.Textbox()  # 创建一个文本框组件,用于用户输入消息
    clear = gr.Button("Clear")  # 创建一个清除按钮,用于清空聊天记录


    # 定义处理用户消息的函数
    def user(user_message, history):
        return "", history + [[user_message, None]]  # 用户的消息被添加到历史中,机器人的回复暂时为空


    # 定义机器人回复的函数
    def bot(history):
        bot_message = random.choice(["How are you?", "I love you", "I'm very hungry"])  # 随机选择一个回复消息
        time.sleep(2)  # 模拟延迟,增加交互的真实感
        history[-1][1] = bot_message  # 将选择的消息作为机器人的回复
        return history  # 返回更新后的聊天历史


    # 当用户提交消息时,首先调用user函数,然后调用bot函数
    msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(
        bot, chatbot, chatbot
    )
    # 当点击Clear按钮时,清空聊天记录
    clear.click(lambda: None, None, chatbot, queue=False)
    
demo.queue()  # 启用队列机制,用于管理同时发生的多个请求
demo.launch()  # 启动应用

这段代码创建了一个简单的聊天机器人应用。应用包括一个文本输入框供用户输入消息,一个聊天显示组件给出机器人的回应,以及一个清除按钮用于重置聊天记录。用户每提交一条消息,系统会随机回复用户三个可能的回应之一: "How are you?", "I love you", "I'm very hungry"。这些回应会在用户的消息之后大约2秒钟出现,模拟了一个真实的回复延迟。此外,点击"Clear"按钮会清空聊天记录,为新的对话提供空白页面。通过这一应用,演示了如何使用Gradio构建具有简单交互功能的聊天机器人。

90bc7cee0306388c472380467f46a1e1.png

事件监听器的 .then() 方法会执行后续事件,无论前一个事件是否引发了错误。如果您只想在前一个事件成功执行后才运行后续事件,请使用 .success() 方法,该方法接受与 .then() 相同的参数。

持续运行事件

您可以使用事件监听器的 every 参数按固定时间表运行事件。这将在客户端连接打开时每 every 秒运行一次事件。如果连接关闭,事件将在下一次迭代后停止运行。请注意,这并不考虑事件本身的运行时间。因此,一个运行时间为 1 秒的函数配合 every=5 使用,实际上会每 6 秒运行一次。另请注意,此参数不适用于 js 函数,只适用于与事件监听器关联的 Python 函数。

这是一个每秒更新一次的正弦曲线示例! 

import math  # 导入math库,用于数学计算
import gradio as gr  # 导入gradio库,用于创建UI
import plotly.express as px  # 导入plotly.express,用于作图
import numpy as np  # 导入numpy库,用于处理数值计算


plot_end = 2 * math.pi  # 初始化x轴的结束点为2π


# 定义获取绘图的函数,参数为周期
def get_plot(period=1):
    global plot_end  # 声明全局变量,以便在函数中修改
    x = np.arange(plot_end - 2 * math.pi, plot_end, 0.02)  # 生成x的数据点,从plot_end - 2π到plot_end
    y = np.sin(2*math.pi*period * x)  # 根据周期计算y的值,得到正弦波
    fig = px.line(x=x, y=y)  # 使用plotly.express绘制线图
    plot_end += 2 * math.pi  # 更新plot_end,每次调用函数时将x轴的范围向后移动2π
    if plot_end > 1000:  # 如果plot_end超过1000,重置为2π,避免数值过大
        plot_end = 2 * math.pi
    return fig  # 返回绘图对象


with gr.Blocks() as demo:  # 使用gr.Blocks构建应用布局
    with gr.Row():  # 在一行内布局内容
        with gr.Column():  # 创建一个列
            gr.Markdown("Change the value of the slider to automatically update the plot")  # 用Markdown组件添加文字说明
            period = gr.Slider(label="Period of plot", value=1, minimum=0, maximum=10, step=1)  # 创建一个滑动条用于调节周期
            plot = gr.Plot(label="Plot (updates every half second)")  # 创建一个绘图组件


    # 调用demo.load方法,加载get_plot函数并设置自动刷新参数
    dep = demo.load(get_plot, None, plot, every=1)
    # 当周期滑动条值变化时,调用get_plot函数更新绘图,并设置自动刷新参数,取消之前的自动刷新依赖
    period.change(get_plot, period, plot, every=1, cancels=[dep])


if __name__ == "__main__":
    demo.queue().launch()  # 启用队列并启动应用

这段代码创建了一个交互式Web应用,用于显示不同周期的正弦波图。它通过gradio库创建用户界面,通过plotly.express库绘制正弦波图,使用numpy生成数据。用户可以通过调整滑动条来改变正弦波的周期,该应用会自动重新绘制正弦波图形。在初始状态下,x轴的范围是一个2π的周期,每次更新时,x轴的范围会向后移动2π,直到达到1000后重新开始。这使得用户可以观察到随着时间推移周期性变化的正弦波形状。

d33c63ac115697e46968df58ec223eb4.png

收集事件数据

您可以通过在事件监听函数中为参数添加关联的事件数据类作为类型提示来收集有关事件的特定数据。

例如,.select()的事件数据可以通过gradio.SelectData参数进行类型提示。当用户选择触发组件的某个部分时,会触发此事件,事件数据包括用户具体选择的信息。如果用户在文本框中选择了特定的单词,在画廊中选择了特定的图片,或者在DataFrame中选择了特定的单元格,事件数据参数将包含有关特定选择的信息。

在下面的两人井字棋演示中,用户可以选择DataFrame中的一个单元格来进行移动。事件数据参数包含了被选择的特定单元格的信息。我们可以首先检查该单元格是否为空,然后用用户的移动更新该单元格。

import gradio as gr  # 导入gradio库,用于快速创建Web应用界面


with gr.Blocks() as demo:  # 使用gr.Blocks构建应用的布局
    turn = gr.Textbox("X", interactive=False, label="Turn")  # 创建一个显示当前轮次的玩家的文本框
    board = gr.Dataframe(value=[["", "", ""]] * 3, interactive=False, type="array")  # 创建一个3x3的棋盘,初始为空


    # 定义放置棋子的函数
    def place(board, turn, evt: gr.SelectData):
        if evt.value:  # 若当前选择的单元格已有棋子,则不做操作
            return board, turn
        board[evt.index[0]][evt.index[1]] = turn  # 在棋盘上放置当前玩家的棋子
        turn = "O" if turn == "X" else "X"  # 轮换玩家
        return board, turn  # 返回更新后的棋盘和轮次玩家


    # 当棋盘上的任意一个单元格被选中时,调用place函数
    board.select(place, [board, turn], [board, turn], show_progress="hidden")  # 将place函数绑定到棋盘的选择事件上


demo.launch()  # 启动Web应用

这段代码创建了一个简单的井字棋游戏。游戏界面由一个3x3的棋盘组成,每个格子可以被选中放置棋子,此外还有一个文本框显示当前轮次的玩家("X"或"O")。游戏开始时,由玩家"X"先手。玩家通过点击棋盘上的空格来放置棋子,放置后自动轮换到另一个玩家。如果玩家试图放棋子到已经放置过棋子的格子上,操作不会被允许。这是一个很基础的实现,没有加入胜负的判断逻辑,主要展示了使用Gradio进行交互式Web应用开发的基本方式。

4a3ee3dd9bad1621dd80da594af6991c.png

将多个触发器绑定到一个函数 

通常,您可能希望将多个触发器绑定到同一个函数。例如,您可能希望允许用户点击提交按钮,或按回车提交表单。您可以使用 gr.on 方法并将触发器列表传递给 trigger 。

import gradio as gr  # 导入gradio库


with gr.Blocks() as demo:  # 使用gr.Blocks作为布局容器
    name = gr.Textbox(label="Name")  # 创建一个输入框,用于输入名字
    output = gr.Textbox(label="Output Box", interactive=False)  # 创建一个输出框,用于显示问候语
    greet_btn = gr.Button("Greet")  # 创建一个按钮,点击后会触发问候
    trigger = gr.Textbox(label="Trigger Box", interactive=False)  # 创建一个文本框显示触发事件的组件名
    trigger2 = gr.Textbox(label="Trigger Box", interactive=False)  # 另一个文本框,显示清除操作触发的组件名


    # 定义问候的函数,它会接收名字和事件数据,返回问候语和触发该函数的组件类名
    def greet(name, evt_data: gr.EventData):
        return "Hello " + name + "!", evt_data.target.__class__.__name__
    
    # 定义清除名字的函数,它会返回空字符串和触发该函数的组件类名
    def clear_name(evt_data: gr.EventData):
        return "", evt_data.target.__class__.__name__
    
    # 绑定事件和函数,设置当name提交或greet_btn被点击时,执行greet函数
    # greet函数的输出会分别传给output和trigger文本框
    # 然后继续执行clear_name函数,其输出将用于清空name和填充trigger2文本框
    gr.on(
        triggers=[name.submit, greet_btn.click],  # 设置触发器,name的提交和greet_btn的点击事件
        fn=greet,  # 设置需要执行的函数
        inputs=name,  # 设置函数的输入
        outputs=[output, trigger],  # 设置函数执行后的输出位置
    ).then(clear_name, outputs=[name, trigger2])  # 结合.then方法,使得greet执行完毕后执行clear_name函数


demo.launch()  # 启动应用

这段代码实现了一个简单的问候应用。用户在名字输入框输入名字后,可以通过按下“Greet”按钮或直接提交输入框的内容来触发问候语的生成。问候语会在一个输出框中显示“Hello [输入的名字]!”,同时另一个输出框(Trigger Box)会显示触发问候语生成的组件的类名(Button或Textbox)。问候语生成后,原名字输入框会被清空,另一个Trigger Box会显示用于清除名字的组件的类名(同样是Button或Textbox)。这个应用以一种互动的方式展示了Gradio事件触发、函数绑定、以及事件链(.then)的使用。

482282f1677913bcbad08a31327038b2.png

您也可以使用装饰器语法:

import gradio as gr  # 导入gradio库


with gr.Blocks() as demo:  # 使用gr.Blocks创建布局容器
    name = gr.Textbox(label="Name")  # 创建一个文本输入框,用于输入名字
    output = gr.Textbox(label="Output Box", interactive=False)  # 创建一个文本输出框,用于显示问候语
    greet_btn = gr.Button("Greet")  # 创建一个按钮,点击时将触发问候函数


    # 定义问候函数,该函数将会生成一个问候语
    @gr.on(triggers=[name.submit, greet_btn.click], inputs=name, outputs=output)
    def greet(name):
        return "Hello " + name + "!"  # 返回问候语,格式为"Hello [输入的名字]!"


demo.launch()  # 启动应用

这段代码利用Gradio库快速创建了一个简单的Web应用,用于实现一个基本的问候功能。用户通过输入自己的名字并按下“Greet”按钮或提交文本输入框,应用会在“Output Box”中显示"Hello [输入的名字]!"的问候语。这个示例展示了利用@gr.on装饰器简化事件处理逻辑,实现在特定操作(如点击按钮或提交输入框)后执行特定的函数。通过这种方式,Gradio使得开发交互式Web应用变得更加直观和简单。

您可以使用 gr.on 创建“实时”事件,方法是绑定到实现它的组件的更改事件。如果您不指定任何触发器,该函数将自动绑定到所有包含更改事件的输入组件的所有更改事件(例如 gr.Textbox 有一个更改事件,而 gr.Button 则没有)。

import gradio as gr  # 导入gradio库


with gr.Blocks() as demo:  # 使用gr.Blocks构建应用布局
    with gr.Row():  # 使用gr.Row创建一行,使滑块并排显示
        num1 = gr.Slider(1, 10)  # 创建一个滑块,范围从1到10
        num2 = gr.Slider(1, 10)  # 再创建一个范围为1到10的滑块
        num3 = gr.Slider(1, 10)  # 创建第三个滑块,范围同样为1到10
    output = gr.Number(label="Sum")  # 创建一个用于显示和的数值输出框


    # 定义求和的函数,该函数将会计算三个滑块值的和
    @gr.on(inputs=[num1, num2, num3], outputs=output)  # 使用@gr.on装饰器绑定函数与组件
    def sum(a, b, c):
        return a + b + c  # 返回三个数值的和


demo.launch()  # 启动应用

这段代码创建了一个使用Gradio库构建的简单Web应用,用于计算三个范围在1到10之间的滑块所代表的数值的和。每个滑块允许用户选择一个数值,当任一滑块的值发生变化时,应用会自动调用sum函数,计算这三个数值的和,并将计算结果显示在标签为"Sum"的输出框中。这个示例展示了如何使用Gradio创建交互式Web应用,以及如何利用装饰器@gr.on来将函数与输入输出组件关联起来,从而实现响应用户交互的逻辑。‍

90cc09adc3b137add3755523fe53f492.png

您可以像使用任何常规事件监听器一样,使用 .then 来跟随 gr.on。这个便捷的方法应该可以帮助您避免编写大量重复的代码!

  • 8
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值