【Gradio】初识Gradio

前言

我们经常在网上看到这样

或者这样的界面

可能经常使用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

本文同步自微信公众号 "程序员小溪" ,这里只是同步,想看及时消息请移步我的公众号,不定时更新我的学习经验。

相关推荐
王中阳Go15 小时前
挑战一周用 AI 开发商业化项目!4 大痛点反思 + 新手专属提示词分享
后端·aigc·openai
win4r15 小时前
🚀彻底改写浏览器自动化!ChatGPT Atlas浏览器深度评测:Agent模式自动操作网页太疯狂!跨网页理解+长文秒懂+图片识别,论文工作流被改写!看完你会
chatgpt·aigc·openai
猫头虎17 小时前
DeepSeek刚刚开源了一个3B的 OCR模型:什么是DeepSeek-OCR?单张A100-40G每天可以处理20万+页文档
人工智能·开源·whisper·prompt·aigc·ocr·gpu算力
自由生长20241 天前
使用AI的思考-为什么大模型不会主动提问
aigc·ai编程
Mintopia1 天前
⚖️ AIGC版权确权技术:Web内容的AI生成标识与法律适配
前端·javascript·aigc
用户5191495848452 天前
使用Python ConfigParser解析INI配置文件完全指南
人工智能·aigc
小溪彼岸2 天前
分享一个Claude Code宝藏网站Claude Code Templates
aigc·claude
YFCodeDream2 天前
MLLM技术报告 核心创新一览
python·gpt·aigc
蛋先生DX2 天前
RAG 切片利器 LumberChunker 是如何智能地把文档切割成 LLM 爱吃的块
llm·aigc·ai编程
土丁爱吃大米饭2 天前
AIGC工具助力2D游戏美术全流程
aigc·小游戏·游戏开发·ai助力