前言
我们经常在网上看到这样
或者这样的界面
可能经常使用AI文生图或者文生视频的小伙伴比较熟悉,不知道有没有人好奇这些都是用什么框架做的,反正我好奇了,经过了解找到了今天的主角Gradio,还有另一个框架下次有时间再学习。
Gradio简介
Gradio提供轻量化的机器学习交互式web页面定制工具,为开发者迅速定制AI应用提供快速上手的脚手架。
官网地址:www.gradio.app
Gradio官方文档:www.gradio.app/guides/quic…
安装
Gradio 需要 Python 3.10 或更高版本 |
Gradio安装也很简单,只需要一行命令
$ pip install gradio#为了更快安装,可以使用清华镜像源$ pip install -i https://pypi.tuna.tsinghua.edu.cn/simple gradio
预构建组件
Gradio 包括 30 多个预构建的组件(以及许多社区构建的自定义组件 ),这些组件可用作演示中的输入或输出,这里探索一些常用的组件使用及效果,只有了解组件才知道构建边界。
Textbox(文本框)
创建一个文本区域供用户输入字符串或显示字符串输出。
import gradio as grdef 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还可以监听输入内容改变
# 使用 Blocks 上下文with gr.Blocks() as demo: textbox = gr.Textbox(label="输入") output_text = gr.Textbox(label="输出") textbox.change(textbox_demo, inputs=textbox, outputs=output_text)
甚至还可以实现文本对比功能
from difflib import Differdef 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(数字)
创建一个数值输入字段,供用户输入数字作为输入或显示数值输出。
import gradio as grdef update_number(number): return number * 2with 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
普通按钮
import gradio as grdef 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的主题类型
upload_btn = gr.Button("Upload Results", variant="primary")
ClearButton(清除按钮)
ClearButton可以清除组件或者组建列表的值
import gradio as grdef button_click(name: str|None): return "Hello " + name + "!!"# clearButtonwith 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(上传|下载按钮)
from pathlib import Pathimport gradio as grdef 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(单选框)
创建一组(字符串或数值类型)单选按钮,其中只能选择一个。
import gradio as grdef 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(复选框)
import gradio as grdef 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(图片)
图像组件,可用于上传图像(作为输入)或显示图像(作为输出)
import gradio as grdemo = gr.Interface( fn=lambda x: x, inputs=gr.Image(type="filepath"), outputs=gr.Image(type="filepath"),)if __name__ == "__main__": demo.launch()
图片添加滤镜
import numpy as npimport gradio as grdef 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_imgdemo = gr.Interface(sepia, gr.Image(), "image")if __name__ == "__main__": demo.launch()
ImageEditor(图片编辑)
创建一个图像组件,作为输入时,可用于使用简单的编辑工具(如画笔、笔触、裁剪和图层)上传和编辑图像。作为输出时,该组件可用于显示图像。
import gradio as grimport timedef 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(图片滑块)
滑动展示图片模糊对比
import gradio as grfrom PIL import ImageFilterdef 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 imdef 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代码编辑器,用于查看代码(作为输出组件),或用于输入和编辑代码(作为输入组件)。
import gradio as grdef code_demo(code): return code# 方法1:使用 Interface# demo = gr.Interface(fn=code_demo, inputs="code", outputs="text")# 方法2:使用 Blockswith 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(颜色选择器)
颜色选择器让用户选择颜色作为字符串输入。可以用作输入,将颜色值传递给函数,或用作输出,显示颜色值。
import gradio as grimport numpy as npfrom PIL import Image, ImageColorimport redef 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_imagedemo = 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 。
import gradio as grdef 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 布局中渲染子组件。
import gradio as grwith gr.Blocks() as demo: with gr.Sidebar(): gr.Textbox() gr.Button() gr.Textbox()if __name__ == "__main__": demo.launch()
DateTime(时间选择器)
该组件有兼容问题,不兼容Safari内核
DateTime时间选择器用于选择日期(可选)和时间的选择组件。
import gradio as grdef 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(作为输出使用时)
单文件上传
import gradio as grimport osdef 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()
多文件上传
multiple_files = gr.File(label="上传多个文件",file_count="multiple")
指定文件类型上传
# pdfpdf_file = gr.File(label="上传PDF文件",file_types=[".pdf"])# 图片|视频media_files = gr.File(label="上传图片或视频文件",file_types=["image", "video"])
文件预览
import gradio as grdef preview_and_prepare_download(file): if file is None: return None return filewith 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 应用的机器上的文件。作为输入组件,它还允许用户选择文件用作函数的输入,而作为输出组件,它显示所选文件。
import gradio as grdef 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(音频)
import numpy as npimport gradio as grnotes = ["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, audiodemo = 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(画廊)
画廊组件允许显示图像或视频的网格,如果用作输入,用户可以将图像或视频上传到画廊。如果用作输出,用户可以点击单个图像或视频以查看更高分辨率的版本。
import randomimport gradio as grdef 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 imageswith 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 输出。由于这个组件不接受用户输入,它很少被用作输入组件。
import gradio as grwith 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。由于这个组件不接受用户输入,它很少被用作输入组件。
import gradio as grwith 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 输出。由于该组件不接受用户输入,因此很少用作输入组件。
from zipfile import ZipFileimport gradio as grdef 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 filesdemo = gr.Interface(zip_to_json, "file", "json")if __name__ == "__main__": demo.launch()
Label(分类标签)
显示分类标签以及提供的顶级类别的置信度分数。由于此组件不接受用户输入,因此很少用作输入组件。
import gradio as grwith 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(多模态文本框)
创建一个文本区域供用户输入字符串或显示字符串输出,并允许上传多媒体文件。
import gradio as grimport 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 historywith 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(表格组件)
显示一个表格组件,可以用作输出组件来显示数据,或作为输入组件来收集用户数据。
import gradio as grdef tax_calculator(income, assets): total_deductible = sum(assets["Cost"]) taxable_income = income - total_deductible return taxable_incomedemo = 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(参数查看器)
以表格形式展示组件参数
import gradio as grwith 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(状态)
用于表单中存储任意类型的值,避免破坏分组结构
import gradio as grdef 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 事件监听器定期触发事件。
import gradio as grimport timewith 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 的数据
import gradio as grimport pandas as pdfrom random import randint, randomfood_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 的数据。
import gradio as grimport pandas as pdfrom random import randinttemp_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(图表)
import pandas as pdimport numpy as npimport gradio as grdef 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 dfdemo = 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()
import matplotlib.pyplot as pltimport numpy as npimport gradio as grdef 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 figdemo = 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 的数据。
import pandas as pdfrom random import randint, randomimport gradio as grfood_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()
基本使用
简单示例
import gradio as grdef 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初始化必须指定三个参数:事件处理函数、输入组件、输出组件。
简单使用
我们可以使用如下方式快速构建一个表单界面,默认会自带提交按钮
import gradio as grdef image_classifier(inp): return {'cat': 0.3, 'dog': 0.7}demo = gr.Interface(fn=image_classifier, inputs="image", outputs="label")demo.launch()
Examples
import gradio as grdef 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支持多输入输出,可以构建出更为复杂的界面
import gradio as grdef 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提供更灵活的界面构建方式,使用如下方式即可快速构建一个简单界面
import gradio as grwith 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(队列)
通过启用队列,可以控制在队列中的位置,并设置允许的最大事件数量限制。
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(加载)
import gradio as grdef 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(卸载)
当用户关闭或刷新标签页时,此监听器会被触发,结束用户会话。在应用关闭时清理资源时,它很有用。
import gradio as grwith 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 的高级抽象,允许你用几行代码围绕聊天机器人模型创建一个基于网络的演示。
import gradio as grdef echo(message, history): return messagedemo = gr.ChatInterface( fn=echo, type="messages", examples=["hello", "hola", "merhaba"], title="Echo Bot")demo.launch()
自定义机器人
import gradio as grdef 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 属性)不会被加载。
import gradio as grhello_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 应用程序。
$ gradio app.py
分享示例
import gradio as grdef greet(name): return "Hello " + name + "!"# 创建一个简单的接口demo = gr.Interface(fn=greet, inputs="textbox", outputs="textbox")# 启动服务demo.launch(share=True)
按照提示下载依赖文件、重命名、移动位置,最后为依赖文件添加权限
# 进入/Users/username/.cache/huggingface/gradio/frpc目录下为依赖文件添加权限$ chmod +x frpc_darwin_amd64_v0.3
最新运行代码,可以看到生成的分享链接(链接有效期为1周)
在浏览器中打开,效果如下:
还可以为分享添加授权
import gradio as grdef 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 目录下为依赖文件添加权限
$ chmod +x frpc_darwin_amd64_v0.3
友情提示
见原文:【Gradio】初识Gradio
本文同步自微信公众号 "程序员小溪" ,这里只是同步,想看及时消息请移步我的公众号,不定时更新我的学习经验。