前言
我们经常在网上看到这样

或者这样的界面
可能经常使用AI文生图或者文生视频的小伙伴比较熟悉,不知道有没有人好奇这些都是用什么框架做的,反正我好奇了,经过了解找到了今天的主角Gradio,还有另一个框架下次有时间再学习。
Gradio简介
Gradio提供轻量化的机器学习交互式web页面定制工具,为开发者迅速定制AI应用提供快速上手的脚手架。
官网地址:www.gradio.app

Gradio官方文档:www.gradio.app/guides/quic...
安装
|-----------------------------|
| Gradio 需要 Python 3.10 或更高版本 |
Gradio安装也很简单,只需要一行命令
php
$ pip install gradio
#为了更快安装,可以使用清华镜像源
$ pip install -i https://pypi.tuna.tsinghua.edu.cn/simple gradio
预构建组件
Gradio 包括 30 多个预构建的组件(以及许多社区构建的自定义组件 ),这些组件可用作演示中的输入或输出,这里探索一些常用的组件使用及效果,只有了解组件才知道构建边界。
Textbox(文本框)
创建一个文本区域供用户输入字符串或显示字符串输出。
ini
import gradio as gr
def textbox_demo(text: str|None):
return text
# 方法1:默认类型
demo = gr.Interface(
fn=textbox_demo,
inputs="textbox",
outputs="textbox",
title="文本框示例"
)
# 方法2:使用 Interface 上下文
# demo = gr.Interface(
# fn=textbox_demo,
# inputs=gr.Textbox(label="输入"),
# outputs=gr.Textbox(label="输出"),
# title="文本框示例"
# )
demo.launch()
效果如下所示:
|------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------|
| |
|
Textbox还可以监听输入内容改变
ini
# 使用 Blocks 上下文
with gr.Blocks() as demo:
textbox = gr.Textbox(label="输入")
output_text = gr.Textbox(label="输出")
textbox.change(textbox_demo, inputs=textbox, outputs=output_text)

甚至还可以实现文本对比功能
ini
from difflib import Differ
def diff_texts(text1: str, text2: str):
d = Differ()
return [
(token[2:], token[0] if token[0] != " " else None)
for token in d.compare(text1, text2)
]
demo = gr.Interface(
fn=diff_texts,
inputs=[
gr.Textbox(label="输入1", info="Initial text", lines=3, value="Text 1"),
gr.Textbox(label="输入2", info="Compare text", lines=3, value="Text 2")
],
outputs=[
gr.HighlightedText(
label="Diff",
combine_adjacent=True,
show_legend=True,
color_map={"+" : "red", "-" : "green"}
)
],
title="文本框示例"
)
demo.launch()

Number(数字)
创建一个数值输入字段,供用户输入数字作为输入或显示数值输出。
typescript
import gradio as gr
def update_number(number):
return number * 2
with gr.Blocks() as demo:
number = gr.Number(label="Number")
output = gr.Textbox(label="Output")
number.change(fn=update_number, inputs=number, outputs=output)
if __name__ == "__main__":
demo.launch()

Button
普通按钮
python
import gradio as gr
def button_click(name: str|None):
return "Hello " + name + "!!"
# 方法1:使用 Blocks 上下文
with gr.Blocks() as demo:
name_input = gr.Textbox(label="输入您的姓名")
output_text = gr.Textbox(label="输出")
button = gr.Button("点击我")
button.click(button_click, inputs=name_input, outputs=output_text)
# 方法2:或者直接使用 Interface(注释掉上面的代码,使用下面的)
# demo = gr.Interface(
# fn=button_click,
# inputs=gr.Textbox(label="输入您的姓名"),
# outputs=gr.Textbox(label="输出"),
# title="按钮点击示例"
# )
if __name__ == "__main__":
demo.launch()
效果如下所示:
|------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------|
| |
|
按钮默认是灰色的,也可以通过 variant 修改Button的主题类型
ini
upload_btn = gr.Button("Upload Results", variant="primary")

ClearButton(清除按钮)
ClearButton可以清除组件或者组建列表的值
ini
import gradio as gr
def button_click(name: str|None):
return "Hello " + name + "!!"
# clearButton
with gr.Blocks() as demo:
name_input = gr.Textbox(label="输入您的姓名")
output_text = gr.Textbox(label="输出")
# 普通按钮用于执行功能
submit_button = gr.Button("提交", variant="primary")
# 清除按钮用于清除文本框内容
clear_button = gr.ClearButton(
components=[name_input, output_text],
value="清除"
)
submit_button.click(button_click, inputs=name_input, outputs=output_text)
if __name__ == "__main__":
demo.launch()

UploadButton & DownloadButton(上传|下载按钮)
python
from pathlib import Path
import gradio as gr
def upload_file(filepath):
name = Path(filepath).name
return [gr.UploadButton(visible=False), gr.DownloadButton(label=f"Download {name}", value=filepath, visible=True)]
def download_file():
return [gr.UploadButton(visible=True), gr.DownloadButton(visible=False)]
with gr.Blocks() as demo:
gr.Markdown("First upload a file and and then you'll be able download it (but only once!)")
with gr.Row():
u = gr.UploadButton("Upload a file", file_count="single")
d = gr.DownloadButton("Download the file", visible=False)
u.upload(upload_file, u, [u, d])
d.click(download_file, None, [u, d])
if __name__ == "__main__":
demo.launch()

DuplicateButton(重复按钮)
DuplicateButton为特定平台兼容组件,当 demo 在 Hugging Face Spaces 上运行时触发 Spaces 复制的按钮,本地不执行任何操作。
Radio(单选框)
创建一组(字符串或数值类型)单选按钮,其中只能选择一个。
python
import gradio as gr
def update_location(location):
return f"You selected {location}"
with gr.Blocks() as demo:
radio = gr.Radio(["park", "zoo", "road"], label="Location", info="Where did they go?")
output_text = gr.Textbox(label="Result")
radio.change(fn=update_location, inputs=radio, outputs=output_text)
if __name__ == "__main__":
demo.launch()

Checkbox & CheckboxGroup(复选框)
python
import gradio as gr
def checkbox_demo(morning, fruits):
return "Now is " + ("morning" if morning else "afternoon") + " and I like " + (" and ".join(fruits) if fruits else "nothing")
# 方法1:默认类型
# demo = gr.Interface(
# fn=checkbox_demo,
# inputs="checkbox",
# outputs="text",
# title="Checkbox Demo",
# )
# 方法2:指定类型
demo = gr.Interface(
checkbox_demo,
inputs=[
gr.Checkbox(label="morning"),
gr.CheckboxGroup(
choices=["apples", "bananas", "cherries"],
label="Fruits",
info="Choose your favorite fruit"
),
],
outputs="text",
title="Checkbox Demo",
)
demo.launch()

Image
Image(图片)
图像组件,可用于上传图像(作为输入)或显示图像(作为输出)
ini
import gradio as gr
demo = gr.Interface(
fn=lambda x: x,
inputs=gr.Image(type="filepath"),
outputs=gr.Image(type="filepath"),
)
if __name__ == "__main__":
demo.launch()

图片添加滤镜
ini
import numpy as np
import gradio as gr
def sepia(input_img):
if input_img is None:
return None
sepia_filter = np.array([
[0.393, 0.769, 0.189],
[0.349, 0.686, 0.168],
[0.272, 0.534, 0.131]
])
# 确保输入是float类型,避免溢出
img = input_img.astype(np.float32)
# 应用sepia滤镜
sepia_img = img @ sepia_filter.T
# 保证像素值在0-255之间
sepia_img = np.clip(sepia_img, 0, 255)
sepia_img = sepia_img.astype(np.uint8)
return sepia_img
demo = gr.Interface(sepia, gr.Image(), "image")
if __name__ == "__main__":
demo.launch()

ImageEditor(图片编辑)
创建一个图像组件,作为输入时,可用于使用简单的编辑工具(如画笔、笔触、裁剪和图层)上传和编辑图像。作为输出时,该组件可用于显示图像。
ini
import gradio as gr
import time
def sleep(im):
time.sleep(5)
return [im["background"], im["layers"][0], im["layers"][1], im["composite"]]
def predict(im):
return im["composite"]
with gr.Blocks() as demo:
with gr.Row():
im = gr.ImageEditor(
type="numpy",
crop_size="1:1",
)
im_preview = gr.Image()
n_upload = gr.Number(0, label="Number of upload events", step=1)
n_change = gr.Number(0, label="Number of change events", step=1)
n_input = gr.Number(0, label="Number of input events", step=1)
im.upload(lambda x: x + 1, outputs=n_upload, inputs=n_upload)
im.change(lambda x: x + 1, outputs=n_change, inputs=n_change)
im.input(lambda x: x + 1, outputs=n_input, inputs=n_input)
im.change(predict, outputs=im_preview, inputs=im, show_progress="hidden")
if __name__ == "__main__":
demo.launch()

ImageSlider(图片滑块)
滑动展示图片模糊对比
python
import gradio as gr
from PIL import ImageFilter
def img_to_slider(im):
if not im:
return im
return (im, im.filter(filter=ImageFilter.GaussianBlur(radius=10)))
def slider_to_self(im):
if not im or not im[0]:
return im
return (im[0], im[0].filter(filter=ImageFilter.GaussianBlur(radius=10)))
def slider_to_self_two(im):
return im
def position_to_slider(pos):
return gr.ImageSlider(slider_position=pos)
with gr.Blocks() as demo:
gr.Markdown("## img to image slider")
with gr.Row():
img1 = gr.Image(label="Blur image", type="pil")
img2 = gr.ImageSlider(label="Blur image", type="pil")
btn = gr.Button("Blur image")
btn.click(img_to_slider, inputs=img1, outputs=img2)
gr.Markdown("## unified image slider")
with gr.Row():
img3 = gr.ImageSlider(label="Blur image", type="pil")
img3.upload(slider_to_self, inputs=img3, outputs=img3)
pos = gr.Slider(label="Position", value=50, minimum=0, maximum=100, step=0.01)
pos.change(position_to_slider, inputs=pos, outputs=img3, show_progress="hidden")
if __name__ == "__main__":
demo.launch()


Code(代码)
Code代码编辑器,用于查看代码(作为输出组件),或用于输入和编辑代码(作为输入组件)。
ini
import gradio as gr
def code_demo(code):
return code
# 方法1:使用 Interface
# demo = gr.Interface(fn=code_demo, inputs="code", outputs="text")
# 方法2:使用 Blocks
with gr.Blocks() as demo:
code_input = gr.Code(label="输入您的代码")
output_text = gr.Textbox(label="输出")
submit_button = gr.Button("提交")
submit_button.click(code_demo, inputs=code_input, outputs=output_text)
demo.launch()
|------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------|
| |
|
ColorPicker(颜色选择器)
颜色选择器让用户选择颜色作为字符串输入。可以用作输入,将颜色值传递给函数,或用作输出,显示颜色值。
python
import gradio as gr
import numpy as np
from PIL import Image, ImageColor
import re
def parse_color(color_str):
"""解析颜色字符串,支持 rgba() 和十六进制格式"""
if color_str.startswith('rgba('):
# 解析 rgba(r, g, b, a) 格式
match = re.match(r'rgba\(([^,]+),\s*([^,]+),\s*([^,]+),\s*([^)]+)\)', color_str)
if match:
r, g, b, a = match.groups()
return (int(float(r)), int(float(g)), int(float(b)))
elif color_str.startswith('rgb('):
# 解析 rgb(r, g, b) 格式
match = re.match(r'rgb\(([^,]+),\s*([^,]+),\s*([^)]+)\)', color_str)
if match:
r, g, b = match.groups()
return (int(float(r)), int(float(g)), int(float(b)))
else:
# 尝试使用 PIL 的标准颜色解析
try:
return ImageColor.getcolor(color_str, "RGB")
except ValueError:
# 如果解析失败,返回默认颜色(红色)
return (255, 0, 0)
# 如果所有解析都失败,返回默认颜色
return (255, 0, 0)
def change_color(icon, color):
if icon is None:
return None
img = icon.convert("LA")
img = img.convert("RGBA")
image_np = np.array(icon)
_, _, _, alpha = image_np.T
mask = alpha > 0
# 使用自定义的颜色解析函数
rgb_color = parse_color(color)
image_np[..., :-1][mask.T] = rgb_color
edited_image = Image.fromarray(image_np)
return edited_image
demo = gr.Interface(
fn=change_color,
inputs=[
gr.Image(label="icon", type="pil", image_mode="RGBA"),
gr.ColorPicker(label="color"),
],
outputs=gr.Image(label="colored icon")
)
if __name__ == "__main__":
demo.launch()

Slider(滑块)
创建一个从 minimum 到 maximum 的滑动条,步长为 step 。
python
import gradio as gr
def greet(age: int) -> str:
return f"你今年{age}岁。"
with gr.Blocks() as demo:
input = gr.Slider(minimum=0, maximum=100, step=10, value=50, label="Slider")
output = gr.Textbox(label="Output")
input.change(greet, [input], output)
if __name__ == "__main__":
demo.launch()

SideBar(侧边栏)
SideBar是位于屏幕左侧的侧边栏,是一个可折叠的面板,常用于在 Blocks 布局中渲染子组件。
scss
import gradio as gr
with gr.Blocks() as demo:
with gr.Sidebar():
gr.Textbox()
gr.Button()
gr.Textbox()
if __name__ == "__main__":
demo.launch()

DateTime(时间选择器)
该组件有兼容问题,不兼容Safari内核
DateTime时间选择器用于选择日期(可选)和时间的选择组件。
ini
import gradio as gr
def greet(name, datetime):
return f"{name},你的预约时间是:{datetime}"
demo = gr.Interface(
fn=greet,
inputs=[
gr.Textbox(label="姓名"),
gr.DateTime(label="预约时间")
],
outputs="text"
)
demo.launch()

File
File(上传)
创建一个文件组件,允许上传一个或多个通用文件(作为输入使用时)或显示通用文件或下载 URL(作为输出使用时)
单文件上传
ini
import gradio as gr
import os
def process_single_file(file):
if file is None:
return "没有上传文件", None
file_info = f"""
文件名: {file.name}
文件大小: {os.path.getsize(file.name) if os.path.exists(file.name) else "未知"} bytes
文件路径: {file.name}
"""
return file_info, file
# 单个文件上传
with gr.Blocks() as demo:
single_file = gr.File(label="上传文件", file_count="single")
single_btn = gr.Button("处理文件")
single_output = gr.Textbox(label="文件信息")
single_file_output = gr.File(label="处理后的文件")
single_btn.click(process_single_file, inputs=single_file, outputs=[single_output, single_file_output])
demo.launch()

多文件上传
ini
multiple_files = gr.File(label="上传多个文件",file_count="multiple")
指定文件类型上传
ini
# pdf
pdf_file = gr.File(label="上传PDF文件",file_types=[".pdf"])
# 图片|视频
media_files = gr.File(label="上传图片或视频文件",file_types=["image", "video"])
文件预览
ini
import gradio as gr
def preview_and_prepare_download(file):
if file is None:
return None
return file
with gr.Blocks() as demo:
with gr.Row():
with gr.Column():
preview_file = gr.File(
label="上传文件进行预览",
file_count="single"
)
with gr.Column():
file_preview = gr.File(
label="文件预览",
interactive=False # 只预览文件
)
preview_file.change(
preview_and_prepare_download,
inputs=preview_file,
outputs=[file_preview]
)
if __name__ == "__main__":
demo.launch()

FileExplorer(文件浏览器)
创建一个文件浏览器组件,允许用户浏览托管 Gradio 应用的机器上的文件。作为输入组件,它还允许用户选择文件用作函数的输入,而作为输出组件,它显示所选文件。
ini
import gradio as gr
def show_selected_files(files):
if not files:
return "未选择任何文件"
info = []
for f in files:
# gr.FileExplorer 返回的是文件路径字符串,而不是带有 .name 属性的对象
info.append(f"文件名: {f.split('/')[-1]}\n路径: {f}\n")
return "\n".join(info)
with gr.Blocks() as demo:
with gr.Row():
with gr.Column():
file_explorer = gr.FileExplorer(
label="选择文件或文件夹",
root_dir=".", # 可以根据需要设置根目录
file_count="multiple"
)
with gr.Column():
selected_files_info = gr.Textbox(
label="选中文件信息",
lines=8
)
file_explorer.change(
show_selected_files,
inputs=file_explorer,
outputs=selected_files_info
)
demo.launch()

Audio(音频)
ini
import numpy as np
import gradio as gr
notes = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
def generate_tone(note, octave, duration):
sr = 48000
a4_freq, tones_from_a4 = 440, 12 * (octave - 4) + (note - 9)
frequency = a4_freq * 2 ** (tones_from_a4 / 12)
duration = int(duration)
audio = np.linspace(0, duration, duration * sr)
audio = (20000 * np.sin(audio * (2 * np.pi * frequency))).astype(np.int16)
return sr, audio
demo = gr.Interface(
generate_tone,
[
gr.Dropdown(notes, type="index"),
gr.Slider(4, 6, step=1),
gr.Textbox(value="1", label="Duration in seconds"),
],
"audio",
)
if __name__ == "__main__":
demo.launch()

Gallery(画廊)
画廊组件允许显示图像或视频的网格,如果用作输入,用户可以将图像或视频上传到画廊。如果用作输出,用户可以点击单个图像或视频以查看更高分辨率的版本。
ini
import random
import gradio as gr
def fake_gan():
images = [
(random.choice(
[
"http://www.marketingtool.online/en/face-generator/img/faces/avatar-1151ce9f4b2043de0d2e3b7826127998.jpg",
"http://www.marketingtool.online/en/face-generator/img/faces/avatar-116b5e92936b766b7fdfc242649337f7.jpg",
"http://www.marketingtool.online/en/face-generator/img/faces/avatar-1163530ca19b5cebe1b002b8ec67b6fc.jpg",
"http://www.marketingtool.online/en/face-generator/img/faces/avatar-1116395d6e6a6581eef8b8038f4c8e55.jpg",
"http://www.marketingtool.online/en/face-generator/img/faces/avatar-11319be65db395d0e8e6855d18ddcef0.jpg",
]
), f"label {i}")
for i in range(3)
]
return images
with gr.Blocks() as demo:
gallery = gr.Gallery(
label="Generated images", show_label=False, elem_id="gallery"
, columns=2, object_fit="contain", height="200px")
btn = gr.Button("Generate images", scale=0)
btn.click(fake_gan, None, gallery)
if __name__ == "__main__":
demo.launch()

HTML(展示HTM L)
用来显示任意的 HTML 输出。由于这个组件不接受用户输入,它很少被用作输入组件。
ini
import gradio as gr
with gr.Blocks() as demo:
input_text = gr.Textbox(placeholder="Enter text.")
scroll_btn = gr.Button("Scroll")
no_scroll_btn = gr.Button("No Scroll")
big_block = gr.HTML("""
<div style='height: 100px; width: 100px; background-color: pink;'></div>
<div style='height: 100px; width: 100px; background-color: blue;'></div>
<div style='height: 100px; width: 100px; background-color: green;'></div>
<div style='height: 100px; width: 100px; background-color: yellow;'></div>
<div style='height: 100px; width: 100px; background-color: red;'></div>
""")
out = gr.Textbox()
scroll_btn.click(
lambda x: x,
inputs=input_text,
outputs=out,
scroll_to_output=True
)
no_scroll_btn.click(
lambda x: x,
inputs=input_text,
outputs=out,
scroll_to_output=False
)
if __name__ == "__main__":
demo.launch()

Markdown(展示Markdown)
用于渲染任意 Markdown 输出。也可以渲染被美元符号包围的 LaTeX。由于这个组件不接受用户输入,它很少被用作输入组件。
python
import gradio as gr
with gr.Blocks() as demo:
gr.Markdown(
"""
# Hello World!
Start typing below to see the output.
## 这是一个标题
### 这是一个副标题
#### 这是一个小标题
""")
gr.Markdown(
"""
# 图片
<img src="http://www.marketingtool.online/en/face-generator/img/faces/avatar-1151ce9f4b2043de0d2e3b7826127998.jpg" alt="alt text" width="200" height="200"/>
""")
if __name__ == "__main__":
demo.launch()

JSON(JSON美化)
用于美观地显示任意 JSON 输出。由于该组件不接受用户输入,因此很少用作输入组件。
python
from zipfile import ZipFile
import gradio as gr
def zip_to_json(file_obj):
files = []
with ZipFile(file_obj.name) as zfile:
for zinfo in zfile.infolist():
files.append(
{
"name": zinfo.filename,
"file_size": zinfo.file_size,
"compressed_size": zinfo.compress_size,
}
)
return files
demo = gr.Interface(zip_to_json, "file", "json")
if __name__ == "__main__":
demo.launch()

Label(分类标签)
显示分类标签以及提供的顶级类别的置信度分数。由于此组件不接受用户输入,因此很少用作输入组件。
ini
import gradio as gr
with gr.Blocks() as demo:
label1 = gr.Label(
value={"A类": 0.8, "B类": 0.15, "C类": 0.05},
label="分类结果"
)
label2 = gr.Label(
value={"A类": 0.8, "B类": 0.15, "C类": 0.05},
label="分类结果",
num_top_classes=2 # 只显示前2个
)
if __name__ == "__main__":
demo.launch()

MultimodalTextbox(多模态文本框)
创建一个文本区域供用户输入字符串或显示字符串输出,并允许上传多媒体文件。
python
import gradio as gr
import time
# Chatbot demo with multimodal input (text, markdown, LaTeX, code blocks, image, audio, & video). Plus shows support for streaming text.
def print_like_dislike(x: gr.LikeData):
print(x.index, x.value, x.liked)
def add_message(history, message):
for x in message["files"]:
history.append({"role": "user", "content": {"path": x}})
if message["text"] is not None:
history.append({"role": "user", "content": message["text"]})
return history, gr.MultimodalTextbox(value=None, interactive=False)
def bot(history: list):
response = "**That's cool!**"
history.append({"role": "assistant", "content": ""})
for character in response:
history[-1]["content"] += character
time.sleep(0.05)
yield history
with gr.Blocks() as demo:
chatbot = gr.Chatbot(elem_id="chatbot", bubble_full_width=False, type="messages")
chat_input = gr.MultimodalTextbox(
interactive=True,
file_count="multiple",
placeholder="Enter message or upload file...",
show_label=False,
sources=["microphone", "upload"],
)
chat_msg = chat_input.submit(
add_message,
[chatbot, chat_input],
[chatbot, chat_input]
)
bot_msg = chat_msg.then(bot, chatbot, chatbot, api_name="bot_response")
bot_msg.then(lambda: gr.MultimodalTextbox(interactive=True), None, [chat_input])
chatbot.like(print_like_dislike, None, None, like_user_message=True)
if __name__ == "__main__":
demo.launch()

DataFrame(表格组件)
显示一个表格组件,可以用作输出组件来显示数据,或作为输入组件来收集用户数据。
ini
import gradio as gr
def tax_calculator(income, assets):
total_deductible = sum(assets["Cost"])
taxable_income = income - total_deductible
return taxable_income
demo = gr.Interface(
fn=tax_calculator,
inputs=[
gr.Number(label="Income"),
gr.Dataframe(
headers=["Item", "Cost"],
datatype=["str", "number"],
label="Assets Purchased this Year",
)
],
outputs="text",
examples=[
[100000, [["Suit", 5000], ["Laptop", 800], ["Car", 1800]]],
[100000, [["Suit", 5000], ["Watch", 500], ["Car", 1800]]],
],
)
if __name__ == "__main__":
demo.launch()

ParamViewer(参数查看器)
以表格形式展示组件参数
python
import gradio as gr
with gr.Blocks() as demo:
gr.ParamViewer(
{
"iterable": {
"type": "Iterable",
"description": "可迭代对象,如列表、元组等",
"default": None
},
"key": {
"type": "function | None",
"description": "用于比较的函数",
"default": "None"
},
"default": {
"type": "Any",
"description": "当可迭代对象为空时返回的默认值",
"default": "None"
}
}
)
if __name__ == "__main__":
demo.launch()

State(状态)
用于表单中存储任意类型的值,避免破坏分组结构
ini
import gradio as gr
def add_message(message, history):
"""添加消息到历史记录"""
if not history:
history = []
history.append(message)
return history, "" # 返回更新的历史和清空的输入框
def clear_history():
"""清空消息历史"""
return []
with gr.Blocks() as demo:
# 消息历史状态
message_history_state = gr.State(value=[])
with gr.Row():
message_input = gr.Textbox(
label="输入消息",
placeholder="输入您的消息...",
scale=3
)
history_display = gr.JSON(label="消息历史", value=[])
with gr.Row():
add_msg_btn = gr.Button("📤 发送", scale=1, variant="primary")
clear_history_btn = gr.Button("🗑️ 清空历史", variant="stop")
# 绑定消息相关事件
add_msg_btn.click(
fn=add_message,
inputs=[message_input, message_history_state],
outputs=[message_history_state, message_input]
).then(
fn=lambda x: x,
inputs=message_history_state,
outputs=history_display
)
# 支持回车发送
message_input.submit(
fn=add_message,
inputs=[message_input, message_history_state],
outputs=[message_history_state, message_input]
).then(
fn=lambda x: x,
inputs=message_history_state,
outputs=history_display
)
clear_history_btn.click(
fn=clear_history,
outputs=message_history_state
).then(
fn=lambda x: x,
inputs=message_history_state,
outputs=history_display
)
if __name__ == "__main__":
demo.launch()

Timer(计时器)
一个特殊的组件,在激活时按固定间隔触发。不可见,仅用于通过 tick 事件监听器定期触发事件。
ini
import gradio as gr
import time
with gr.Blocks() as demo:
with gr.Row():
timestamp = gr.Textbox(label="计时器")
with gr.Row():
start_manual_btn = gr.Button("开始计时")
stop_manual_btn = gr.Button("停止计时")
# 创建另一个定时器用于手动控制
manual_timer = gr.Timer(1, active=False) # 初始状态为非活动
manual_timer.tick(lambda: round(time.time()), outputs=timestamp)
start_manual_btn.click(lambda: gr.Timer(active=True), None, manual_timer)
stop_manual_btn.click(lambda: gr.Timer(active=False), None, manual_timer)
if __name__ == "__main__":
demo.launch()

图表
BarPlot(条形图)
创建一个条形图组件来显示来自 pandas DataFrame 的数据
ini
import gradio as gr
import pandas as pd
from random import randint, random
food_rating_data = pd.DataFrame(
{
"cuisine": [["Italian", "Mexican", "Chinese"][i % 3] for i in range(100)],
"rating": [random() * 4 + 0.5 * (i % 3) for i in range(100)],
"price": [randint(10, 50) + 4 * (i % 3) for i in range(100)],
}
)
with gr.Blocks() as demo:
price_by_rating = gr.BarPlot(
food_rating_data,
x="rating",
y="price",
x_bin=1,
)
price_by_rating_color = gr.BarPlot(
food_rating_data,
x="rating",
y="price",
color="cuisine",
x_bin=1,
color_map={"Italian": "red", "Mexican": "green", "Chinese": "blue"},
)
if __name__ == "__main__":
demo.launch()

LinePlot(折线图)
创建一个线形图组件,用于显示来自 pandas DataFrame 的数据。
ini
import gradio as gr
import pandas as pd
from random import randint
temp_sensor_data = pd.DataFrame(
{
"time": pd.date_range("2021-01-01", end="2021-01-05", periods=200),
"temperature": [randint(50 + 10 * (i % 2), 65 + 15 * (i % 2)) for i in range(200)],
"humidity": [randint(50 + 10 * (i % 2), 65 + 15 * (i % 2)) for i in range(200)],
"location": ["indoor", "outdoor"] * 100,
}
)
with gr.Blocks() as demo:
temp_by_time = gr.LinePlot(
temp_sensor_data,
x="time",
y="temperature",
)
temp_by_time_location = gr.LinePlot(
temp_sensor_data,
x="time",
y="temperature",
color="location",
)
if __name__ == "__main__":
demo.launch()

Plot(图表)
ini
import pandas as pd
import numpy as np
import gradio as gr
def plot(v, a):
g = 9.81
theta = a / 180 * 3.14
tmax = ((2 * v) * np.sin(theta)) / g
timemat = tmax * np.linspace(0, 1, 40)
x = (v * timemat) * np.cos(theta)
y = ((v * timemat) * np.sin(theta)) - ((0.5 * g) * (timemat**2))
df = pd.DataFrame({"x": x, "y": y})
return df
demo = gr.Blocks()
with demo:
gr.Markdown(
r"Let's do some kinematics! Choose the speed and angle to see the trajectory. Remember that the range $R = v_0^2 \cdot \frac{\sin(2\theta)}{g}$"
)
with gr.Row():
speed = gr.Slider(1, 30, 25, label="Speed")
angle = gr.Slider(0, 90, 45, label="Angle")
output = gr.LinePlot(
x="x",
y="y",
overlay_point=True,
tooltip=["x", "y"],
x_lim=[0, 100],
y_lim=[0, 60],
width=350,
height=300,
)
btn = gr.Button(value="Run")
btn.click(plot, [speed, angle], output)
if __name__ == "__main__":
demo.launch()

ini
import matplotlib.pyplot as plt
import numpy as np
import gradio as gr
def plot_forecast(final_year, companies, noise, show_legend, point_style):
start_year = 2020
x = np.arange(start_year, final_year + 1)
year_count = x.shape[0]
plt_format = ({"cross": "X", "line": "-", "circle": "o--"})[point_style]
fig = plt.figure()
ax = fig.add_subplot(111)
for i, company in enumerate(companies):
series = np.arange(0, year_count, dtype=float)
series = series**2 * (i + 1)
series += np.random.rand(year_count) * noise
ax.plot(x, series, plt_format)
if show_legend:
plt.legend(companies)
return fig
demo = gr.Interface(
plot_forecast,
[
gr.Radio([2025, 2030, 2035, 2040], label="Project to:"),
gr.CheckboxGroup(["Google", "Microsoft", "Gradio"], label="Company Selection"),
gr.Slider(1, 100, label="Noise Level"),
gr.Checkbox(label="Show Legend"),
gr.Dropdown(["cross", "line", "circle"], label="Style"),
],
gr.Plot(label="forecast", format="png"),
)
if __name__ == "__main__":
demo.launch()

ScatterPlot(散点图)
创建一个散点图组件来显示来自 pandas DataFrame 的数据。
ini
import pandas as pd
from random import randint, random
import gradio as gr
food_rating_data = pd.DataFrame(
{
"cuisine": [["Italian", "Mexican", "Chinese"][i % 3] for i in range(100)],
"rating": [random() * 4 + 0.5 * (i % 3) for i in range(100)],
"price": [randint(10, 50) + 4 * (i % 3) for i in range(100)],
"wait": [random() for i in range(100)],
}
)
with gr.Blocks() as demo:
price_by_rating = gr.ScatterPlot(
food_rating_data,
x="rating",
y="price",
color="wait",
show_actions_button=True,
)
price_by_rating_color = gr.ScatterPlot(
food_rating_data,
x="rating",
y="price",
color="cuisine",
)
if __name__ == "__main__":
demo.launch()

基本使用
简单示例
ini
import gradio as gr
def greet(name, intensity):
return "Hello, " + name + "!" * int(intensity)
demo = gr.Interface(
fn=greet,
inputs=["text", "slider"],
outputs=["text"],
)
demo.launch()
在终端执行 python app.py 启动服务,服务启动后会打印本地服务地址如:http://localhost:7860

Interface类核心参数解释:
- fn : 用于包装用户界面(UI)的函数
- inputs : 用于输入的 Gradio 组件。组件的数量应与你的函数参数数量相匹配。
- outputs : 用于输出的 Gradio 组件。组件的数量应与函数返回值的数量相匹配。
Interface
Interface是 Gradio 的主要高级类,它允许您用几行代码围绕机器学习模型(或任何 Python 函数)创建基于网络的GUI演示。Interface初始化必须指定三个参数:事件处理函数、输入组件、输出组件。
简单使用
我们可以使用如下方式快速构建一个表单界面,默认会自带提交按钮
python
import gradio as gr
def image_classifier(inp):
return {'cat': 0.3, 'dog': 0.7}
demo = gr.Interface(fn=image_classifier, inputs="image", outputs="label")
demo.launch()
Examples
python
import gradio as gr
def sentence_builder(quantity, animal, countries, place, activity_list, morning):
return f"""The {quantity} {animal}s from {" and ".join(countries)} went to the {place} where they {" and ".join(activity_list)} until the {"morning" if morning else "night"}"""
demo = gr.Interface(
sentence_builder,
[
gr.Slider(2, 20, value=4, label="Count", info="Choose between 2 and 20"),
gr.Dropdown(
["cat", "dog", "bird"], label="Animal", info="Will add more animals later!"
),
gr.CheckboxGroup(["USA", "Japan", "Pakistan"], label="Countries", info="Where are they from?"),
gr.Radio(["park", "zoo", "road"], label="Location", info="Where did they go?"),
gr.Checkbox(label="Morning", info="Did they do it in the morning?"),
],
"text",
examples=[
[2, "cat", ["Japan", "Pakistan"], "park", True],
[4, "dog", ["Japan"], "zoo", False],
[10, "bird", ["USA", "Pakistan"], "road", False],
[8, "cat", ["Pakistan"], "zoo", True],
]
)
if __name__ == "__main__":
demo.launch()
examples会为组件提供初始化示例,用户点击示例即可快速填充示例值进行初始化体验

多输入输出
gr.Interface支持多输入输出,可以构建出更为复杂的界面
ini
import gradio as gr
def greet(name, is_morning, temperature):
salutation = "Good morning" if is_morning else "Good evening"
greeting = f"{salutation} {name}. It is {temperature} degrees today"
celsius = (temperature - 32) * 5 / 9
return greeting, round(celsius, 2)
demo = gr.Interface(
fn=greet,
inputs=["text", "checkbox", gr.Slider(0, 100)],
outputs=["text", "number"])
demo.launch()

Blocks
Blocks 是 Gradio 的底层 API,它允许你创建比 Interfaces 更自定义的 Web 应用程序和演示,与 Interface 类相比,Blocks 提供了更多关于以下方面的灵活性和控制权。
简单使用
Blocks提供更灵活的界面构建方式,使用如下方式即可快速构建一个简单界面
scss
import gradio as gr
with gr.Blocks(title="测试") as demo:
gr.Markdown("# 🗂️ 示例")
gr.Markdown("这个示例展示了Gradio Blocks的基本用法")
with gr.Row():
button = gr.Button("确定", variant="primary")
cancel = gr.Button("取消")
demo.launch()
Blocks的灵活也意味着很多事件需要我们自己定义,Blocks构建的页面不会默认带提交按钮

Queue(队列)
通过启用队列,可以控制在队列中的位置,并设置允许的最大事件数量限制。
ini
with gr.Blocks() as demo:
button = gr.Button(label="Generate Image")
button.click(fn=image_generator, inputs=gr.Textbox(), outputs=gr.Image())
# 设置队列上限
demo.queue(max_size=10)
demo.launch()
load(加载)
python
import gradio as gr
def on_page_load():
"""页面加载时执行的函数"""
return "✅ 页面加载完成,欢迎使用!"
with gr.Blocks(title="简单的页面加载示例") as demo:
# 用于显示加载消息的组件
status_box = gr.Textbox(
label="状态",
placeholder="等待页面加载...",
interactive=False
)
# 页面加载事件 - 无输入,只有输出
demo.load(
fn=on_page_load,
outputs=status_box
)
if __name__ == "__main__":
print("启动简单的页面加载示例...")
demo.launch()

unload(卸载)
当用户关闭或刷新标签页时,此监听器会被触发,结束用户会话。在应用关闭时清理资源时,它很有用。
python
import gradio as gr
with gr.Blocks() as demo:
gr.Markdown("# When you close the tab, hello will be printed to the console")
demo.unload(lambda: print("hello"))
demo.launch()
ChatInterface
简单使用
ChatInterface 是 Gradio 用于创建聊天机器人 UI 的高级抽象,允许你用几行代码围绕聊天机器人模型创建一个基于网络的演示。
ini
import gradio as gr
def echo(message, history):
return message
demo = gr.ChatInterface(
fn=echo,
type="messages",
examples=["hello", "hola", "merhaba"],
title="Echo Bot"
)
demo.launch()

自定义机器人
python
import gradio as gr
def yes(message, history):
return "yes"
def vote(data: gr.LikeData):
if data.liked:
print("You upvoted this response: " + str(data.value))
else:
print("You downvoted this response: " + str(data.value))
with gr.Blocks() as demo:
chatbot = gr.Chatbot(placeholder="<strong>Your Personal Yes-Man</strong><br>Ask Me Anything")
chatbot.like(vote, None, None)
gr.ChatInterface(fn=yes, type="messages", chatbot=chatbot)
demo.launch()

TabbedInterface
一个 TabbedInterface 是通过提供一系列 Interface 或 Blocks 创建的,每个 Interface 或 Block 都会在单独的标签页中渲染。只有 Interface/Blocks 中的组件会在标签页中渲染。Blocks 的某些高级属性(例如自定义的 css 、 js 和 head 属性)不会被加载。
ini
import gradio as gr
hello_world = gr.Interface(lambda name: "Hello " + name, "text", "text")
bye_world = gr.Interface(lambda name: "Bye " + name, "text", "text")
chat = gr.ChatInterface(lambda *args: "Hello " + args[0])
demo = gr.TabbedInterface([hello_world, bye_world, chat], ["Hello World", "Bye World", "Chat"])
if __name__ == "__main__":
demo.launch()

Gradio Sketch
只需在终端中键入 gradio sketch 即可打开一个编辑器(相当于一个低代码平台),该编辑器可让您定义和修改 Gradio 组件、调整其布局、添加事件,所有这些都通过 Web 编辑器完成。或者使用这个托管版本的 Gradio Sketch,在 Hugging Face Spaces 上运行 :huggingface.co/spaces/alia...

热重载
Gradio支持热重载,本地开发时只需要在热重载模式下运行 Gradio 应用程序,只要更改文件,该模式就会自动重新加载 Gradio 应用程序。
php
$ gradio app.py

分享示例
python
import gradio as gr
def greet(name):
return "Hello " + name + "!"
# 创建一个简单的接口
demo = gr.Interface(fn=greet, inputs="textbox", outputs="textbox")
# 启动服务
demo.launch(share=True)

按照提示下载依赖文件、重命名、移动位置,最后为依赖文件添加权限
shell
# 进入/Users/username/.cache/huggingface/gradio/frpc目录下为依赖文件添加权限
$ chmod +x frpc_darwin_amd64_v0.3
最新运行代码,可以看到生成的分享链接(链接有效期为1周)

在浏览器中打开,效果如下:

还可以为分享添加授权
ini
import gradio as gr
def reverse(text):
return text[::-1]
demo = gr.Interface(reverse, "text", "text")
demo.launch(share=True, auth=("username", "password"))

输入预置的用户名和密码登录成功后方可进入

生态系统
- Gradio Python 客户端 (gradio_client):在 Python 中以编程方式查询任何 Gradio 应用程序。
- Gradio JavaScript 客户端 (@gradio/client):在 JavaScript 中以编程方式查询任何 Gradio 应用程序。
- Gradio-Lite (@gradio/lite):感谢 Pyodide,用 Python 编写完全在浏览器中运行的 Gradio 应用程序(不需要服务器!)
- Hugging Face Spaces:最受欢迎的托管 Gradio 应用程序的地方 - 免费!
常见问题

进入 /Users/username/.cache/huggingface/gradio/frpc 目录下为依赖文件添加权限
shell
$ chmod +x frpc_darwin_amd64_v0.3
友情提示
见原文:【Gradio】初识Gradio
本文同步自微信公众号 "程序员小溪" ,这里只是同步,想看及时消息请移步我的公众号,不定时更新我的学习经验。