传送门:
- 《Gradio官方教程一:Gradio生态系统、主要组件及Interface class简介》
- 《Gradio 4.37.1官方教程二:Blocks》
- 《Gradio 4.37.1官方教程三:Chatbot》
- 《Gradio 教程四:Building Generative AI Applications with Gradio》
一、Blocks及事件监听器
1.1 Blocks结构
Blocks是Gradio的一个底层API,完全使用Python编写。与Interface类相比,Blocks提供了更多的灵活性和控制,包括:
- 组件的布局
- 触发函数执行的事件
- 处理更复杂的数据流(比如输入可以触发输出,然后触发下一级的输出)
- 演示分组,如使用标签页。
Automatic1111的stable diffusion Web UI就是用Gradio的Blocks构建的,其基本用法如下:
- 创建一个Blocks对象。
- 使用它作为上下文(使用with语句)。
- 在Blocks上下文中定义布局、组件或事件。
- 最后,调用launch()方法启动演示。
通过这些步骤,你可以创建更加复杂和自定义的Web应用程序。下面是一个简单的示例:
import gradio as gr
def update(name):
return f"Welcome to Gradio, {
name}!"
with gr.Blocks() as demo:
gr.Markdown("Start typing below and then click **Run** to see the output.")
with gr.Row():
inp = gr.Textbox(placeholder="What is your name?",label="Name")
out = gr.Textbox(label="Output Box")
btn = gr.Button("Run")
btn.click(fn=update, inputs=inp, outputs=out)
demo.launch()
with gr.Blocks() as demo
:使用with
语句创建了一个名为demo
的Blocks应用程序,所有组件和事件都将在这个上下文中被定义。- 创建了两个文本框组件:name和output。组件与Interface中使用的组件相同。然而,它们不是传递给某个构造函数,而是在with语句块内创建时自动添加到Blocks中。
- 定义按钮组件
btn
btn.click()
:定义按钮点击事件。click()
是事件监听器,定义了应用程序中的数据流。
在上面的示例中,当按钮被点击时,数据流被触发——调用update
函数,name
文本框的内容作为输入,函数返回的结果会显示在output
文本框中。与Interface类似,事件监听器可以接受多个输入或输出。
你也可以通过装饰器的方式定义事件监听器,这样可以跳过 fn
参数,直接分配 inputs
和 outputs
。
import gradio as gr
with gr.Blocks() as demo:
inp = gr.Textbox(placeholder="What is your name?",label="Name")
out = gr.Textbox(label="Output Box")
btn = gr.Button("Run")
@btn.click(inputs=inp, outputs=out)
def update(name):
return f"Welcome to Gradio, {
name}!"
demo.launch()
在上述示例中,你可以编辑Textbox name
,但不能编辑Textbox output
。这是因为作为事件监听器的输入组件默认是交互式的,而输出组件则不是交互式的。你也可以使用interactive
关键字参数覆盖默认行为,直接配置组件的交互性。
out = gr.Textbox(label="Output Box", interactive=True)
如果一个Gradio组件既不是输入也不是输出,它的交互性有两种情况(也可以通过interactive
参数来指定):
- 如果有默认值,则直接是显示内容,默认不可交互。
- 如果没有默认值,那么可以交互。
1.2 事件监听器的类型
import gradio as gr
def welcome(name):
return f"Welcome to Gradio, {
name}!"
with gr.Blocks() as demo:
gr.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)
demo.launch()
gr.Markdown()
:Markdown组件,用于显示标题和说明文字。inp.change(welcome, inp, out)
:change()
事件监听器。每当inp
文本框的内容发生变化时,welcome
函数就会被触发。也就是welcome 函数不是通过点击触发,而是通过在文本框中键入 inp 来触发。
不同的组件支持不同的事件监听器。例如,Video 组件支持 play() 事件侦听器,当用户按下播放按钮时触发。各组件的事件侦听器,详见各组件文档。
1.3 多数据流
Blocks app不像Interfaces 那样仅限于单个数据流,比如下面示例中, num1 可以充当 num2 的输入,反之亦然。
import gradio as gr
def increase(num):
return num + 1
with gr.Blocks() as demo:
a = gr.Number(label="a")
b = gr.Number(label="b")
atob = gr.Button("a > b")
btoa = gr.Button("b > a")
atob.click(increase, a, b)
btoa.click(increase, b, a)
demo.launch()
increase
:将输入数字+1并返回atob.click(increase, a, b)
:点击"a > b"
按钮时将a的数值加1并显示在b中。btoa.click(increase, b, a)
:点击"b > a"
按钮时将b的数值加1并显示在a中。
下面是一个更复杂的示例,一个模型(语音到文本模型)的输出被输入到下一个模型(情感分类器)中。
from transformers import pipeline
import gradio as gr
asr = pipeline("automatic-speech-recognition", "facebook/wav2vec2-base-960h")
classifier = pipeline("text-classification")
def speech_to_text(speech):
text = asr(speech)["text"]
return text
def text_to_sentiment(text):
return classifier(text)[0]["label"]
demo = gr.Blocks()
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()
1.4 多输入组件
如果希望构建包含多个输入组件的demo,有两种选择方式:
-
作为参数列表:输入以列表形式提供,函数的每个参数对应一个输入组件的值。优点是简单直观,缺点是当输入组件数量增多时,管理参数会变得复杂。
-
作为字典传递:字典的键会对应于每个输入组件,值则是该组件当前的值。因为每个组件及其值都是通过键值对明确表示的,所以当包含大量输入组件时,更易管理。
import gradio as gr
with gr.Blocks() as demo:
a = gr.Number(label="a")
b = gr.Number(label="b")
c = gr.Number(label="sum")
with gr.Row():
add_btn = gr.Button("Add")
sub_btn = gr.Button("Subtract")
# 第一种方式:参数列表传递
def add(num1, num2):
return num1 + num2
add_btn.click(add, inputs=[a, b], outputs=c)
# 第二种方式:字典传递
def sub(data):
return data[a] - data[b]
sub_btn.click(sub, inputs={
a, b}, outputs=c)
demo.launch()
1.5 多输出组件
同多输入组件一样,为多个输出组件返回值也有列表和字典两种传递方式。先来看第一种方式:
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 - 1, "full"
else:
return 0, "hungry"
gr.Button("EAT").click(
fn=eat,
inputs=food_box,
outputs=[food_box, status_box]
)
当按钮 "EAT"
被点击时,fn 函数 eat
会被调用,输入是 food_box
的当前值,输出是两个组件:food_box
和 status_box
,输出是按照列表顺序匹配的。当 food >0
时,返回 (food - 1, "full")
;否则返回(0, "hungry")
。
在事件监听器中也可以返回一个字典来更新多个输出组件,而不仅仅是按照顺序返回值列表。通过使用字典返回值,可以有选择地更新某些组件,而跳过其他组件,这在处理复杂条件时特别有用。
import gradio as gr
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]
)
demo.launch()
当food_box > 0
时,同时更新 food_box
和 status_box
。否则只更新 status_box
,而跳过 food_box
的更新。
使用字典返回值时需要注意:尽管字典返回值允许选择性更新组件,但在事件监听器中仍需指定所有可能的输出组件。
1.6 更新组件配置
Gradio允许通过事件监听器函数来更新组件的配置,例如可见性、行数等,而不仅仅是更新组件的值,这适用于需要根据用户选择或交互来动态调整组件外观和行为的场景。
import gradio as gr
def change_textbox(choice):
if choice == "short":
return gr.Textbox(lines=2, visible=True)
elif choice == "long":
return gr.Textbox(lines=8, visible=True, value="Lorem ipsum dolor sit amet")
else:
return gr.Textbox(visible=False)
with gr.Blocks() as demo:
# 构建单选按钮radio
radio = gr.Radio(
["short", "long", "none"], label="What kind of essay would you like to write?"
)
# text是一个文本框,初始设置为2行,具有交互性并显示复制按钮。
text = gr.Textbox(lines=2, interactive=True, show_copy_button=True)
radio.change(fn=change_textbox, inputs=radio, outputs=text)
demo.launch()
上述代码中,radio
是一个单选按钮,有三个宣泄。text
是一个文本框,初始设置为2行,具有交互性并显示复制按钮。radio.change()
函数中,当 radio
组件的值改变时,会触发 change_textbox
函数,这样当用户选择不同的选项时,会返回新的文本框配置,从而更新 text
组件的属性(通过返回新的组件实例,动态更新组件的配置属性)。
1.7 添加示例
使用 gr.Examples
可以方便地为用户提供示例输入,并在需要时缓存这些示例以提高性能。gr.Examples
使用时,需要提供两个参数:
examples
: 一个嵌套列表,外层列表包含每个示例,内层列表包含每个输入组件的对应输入值。inputs
: 需要被示例填充的输入组件或组件列表。
如果设置 cache_examples=True
来缓存示例,则必须提供额外的两个参数:
outputs
:对应示例输出的组件或组件列表fn
:生成示例输出的函数
当启用
cache_examples
时,Gradio会预先计算示例输入对应的输出并将其存储。这意味着在用户点击示例时,不需要实时运行函数来生成输出,从而提高了响应速度。为此,需要知道哪个函数fn
会生成输出,以及这些输出应该填充到哪些outputs
组件中。
import gradio as gr
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":
return num1 / num2
with gr.Blocks() as demo:
with gr.Row():
with gr.Column():
num_1 = gr.Number(value=4)
operation = gr.Radio(["add", "subtract", "multiply", "divide"])
num_2 = gr.Number(value=0)
submit_btn = gr.Button(value="Calculate")
with gr.Column():
result = gr.Number()
submit_btn.click(
calculator, inputs=[num_1,