从 Supabase 数据创建 Gradio 实时仪表盘:完整端到端实战指南
Supabase 是一种基于云端 PostgreSQL 的开源后端服务,提供数据库、身份认证等特性,常用于 Web 与移动应用开发。本指南演示如何把 Supabase 中存储的表格数据接入 Gradio,用原生的 gr.BarPlot 组件在浏览器中构建一个每秒/每分钟自动刷新的实时仪表盘。读完本文,你将掌握创建 Supabase 数据表、使用官方 Python 客户端写入数据、读取数据转成 pandas DataFrame,以及借助 gr.Timer 与 every 参数驱动图表定时刷新的完整链路,从而把任意数据库查询结果变成可分享的实时可视化应用。
Tags: TABULAR(表格数据)、DASHBOARD(仪表盘)、PLOTS(绘图)
前置条件: 需要一个免费的 Supabase 账号(前往 https://app.supabase.com/ 注册即可),并能在本机运行 Python 与 Gradio 环境。
在下面这个端到端指南中,你将学会:
- 在 Supabase 中创建数据表
- 使用 Supabase Python 客户端向表中写入数据
- 使用 Gradio 将数据在实时仪表盘中可视化
如果你的数据已经在 Supabase 中、只想直接做可视化,可以跳过前两个小节,直接跳到可视化部分。
在 Supabase 中创建数据表
首先我们需要一些可以可视化的数据。参考 Supabase 官方推荐的"使用 Python 加载数据"流程,我们创建一份模拟电商数据并写入 Supabase。
- 登录后点击 New Project 按钮,新建一个项目;
- 为项目命名并设置数据库密码,根据需要选择定价计划(本教程使用 Free Tier 免费版即可);
- 在数据库初始化期间(最长约 2 分钟),页面会展示你的 API keys,请妥善保存;
- 点击左侧边栏的 Table Editor(表格图标)创建新表。我们创建一张名为
Product的单表,schema(列结构)如下:
| 列名 | 类型 |
|---|---|
| product_id | int8 |
| inventory_count | int8 |
| price | float8 |
| product_name | varchar |
- 点击 Save 保存表结构。至此数据表准备完成。
说明:
int8即 64 位整数,float8即双精度浮点数,varchar为可变长度字符串——这三类列将来在 pandas 中分别对应整数、浮点与字符串类型,可直接被 Gradio 原生绘图组件识别与展示。
写入数据到 Supabase
接下来,我们用 Supabase 官方 Python 库把数据写入上一步创建的表中。
- 在终端安装
supabase客户端:
pip install supabase
-
获取项目 URL 与 API Key:点击左侧边栏的 Settings(齿轮图标),再点击 API。其中 URL 显示在 Project URL 一栏;API Key 显示在 Project API keys 中(本教程使用带有
service_role、secret标记的密钥)。 -
运行以下 Python 脚本向表内写入模拟数据(需将脚本中的
SUPABASE_URL与SUPABASE_SECRET_KEY替换成第 7 步获取到的真实值):
import supabase
# 初始化 Supabase 客户端
client = supabase.create_client('SUPABASE_URL', 'SUPABASE_SECRET_KEY')
# 定义要写入的数据
import random
main_list = []
for i in range(10):
value = {'product_id': i,
'product_name': f"Item {i}",
'inventory_count': random.randint(1, 100),
'price': random.random()*100
}
main_list.append(value)
# 把数据写入表
data = client.table('Product').insert(main_list).execute()
代码逻辑非常直观:先生成 10 条"商品"记录(product_id 从 0 递增、inventory_count 为 1–100 的随机库存、price 为 0–100 的随机价格),随后通过 client.table('Product').insert(main_list).execute() 一次批量插入。
回到 Supabase 仪表盘并刷新页面,你应该能看到 Product 表里已经填充了 10 行数据。
安全提醒:
service_role密钥绕过行级安全(RLS),拥有对表的最高操作权限。生产环境务必通过环境变量注入、放在服务端,切勿暴露在公开的客户端代码或分享链接中。如需真实项目演示,建议仅用anon公钥并配合 RLS 策略限制访问范围。
可视化数据:Gradio 实时仪表盘
最后,我们仍使用同一个 supabase Python 库从数据集中读取数据,并用 gradio 构建一个实时仪表盘。
提示:本小节会重复上文的某些步骤(如创建 Supabase 客户端),以便你直接跳读。同第 7 步所述,你需要准备数据库对应的 Project URL 与 API Key。
- 编写一个函数,从
Product表中加载数据并返回 pandas DataFrame:
import supabase
import pandas as pd
client = supabase.create_client('SUPABASE_URL', 'SUPABASE_SECRET_KEY')
def read_data():
response = client.table('Product').select("*").execute()
df = pd.DataFrame(response.data)
return df
其中 client.table('Product').select("*").execute() 查询整张 Product 表,response.data 是由字典组成的列表,直接交给 pd.DataFrame() 即可得到结构化表格。
- 创建一个由 2 个柱状图组成的 Gradio 小仪表盘,每分钟绘制一次所有商品的价格与库存,并实时更新:
import gradio as gr
with gr.Blocks() as dashboard:
with gr.Row():
gr.BarPlot(read_data, x="product_id", y="price", title="Prices", every=gr.Timer(60))
gr.BarPlot(read_data, x="product_id", y="inventory_count", title="Inventory", every=gr.Timer(60))
dashboard.queue().launch()
注意这里的关键技巧:向 gr.BarPlot() 传入一个函数而非 DataFrame。这样 Web 应用一加载,柱状图就会立刻查询数据库渲染首帧;随后由于 every 参数指定的刷新节奏,每隔 60 秒会自动重新调用 read_data 并更新图表。运行后,你的最终仪表盘应同时展示两张并排的柱状图——左侧 Prices(横轴 product_id,纵轴 price),右侧 Inventory(横轴 product_id,纵轴 inventory_count)。
源码视角:every、gr.Timer 与刷新机制
这一步之所以"实时",核心在组件基类对 every 的处理。从源码看,Gradio 的原生绘图组件定义于 gradio/components/native_plot.py:BarPlot 继承自 NativePlot,其 get_mark() 方法返回 "bar"(同文件中 LinePlot 返回 "line"、ScatterPlot 返回 "point"),并用它标注图表类型。
NativePlot 构造函数中与实时刷新直接相关的是两个参数(见 native_plot.py):
every: Timer | float | None:若value是函数,则每隔every指定的时间重新调用一次value来重算图表数据;inputs:当value为函数时可传入的输入组件,输入变化时同样触发重算。
every 的底层实现链
关键机制位于组件基类 gradio/components/base.py 的 attach_load_event():
- 传入
value的可调用对象会被识别为load_fn(见 base.py 与get_load_fn_and_initial_value); - 若
every传入的是int/float,会先被包装成Timer对象(every = Timer(every)); - 随后注册
(timer, "tick")事件,即"定时器每个 tick 触发一次重算"; - 组件加载时先执行一次该函数作为首帧数据,之后由 Timer 的
tick事件持续驱动刷新。
gr.Timer 本身定义于 gradio/components/timer.py,构造函数为 Timer(value: float = 1, *, active: bool = True, render: bool = True),其中 value 表示每次 tick 的间隔秒数,active 表示定时器是否激活,render=False 可用于先挂载事件监听、延后渲染的场景。在 timer.py 中可见 Timer 支持 change 与 tick 两类事件。这也是整个"每分钟刷新一次"实时行为的直接来源。
从上述调用链可以推断:本教程写法等价于给每个柱状图注册一个 60 秒周期的 Timer,每到周期触发 read_data → 查询 Supabase → 返回 DataFrame → 前端按 PlotData(mark="bar")重绘。数据写入方只要持续向表中插行,仪表盘就能在一分钟内自动呈现最新状态。
为什么需要 queue()
示例末尾使用了 dashboard.queue().launch()。定时器触发的周期性回调与多组件加载事件属于后台任务/排队请求场景,开启队列(queue)才能保证这些更新事件被后台线程可靠地调度与推送,是构建"数据自动刷新型"应用的推荐做法。
gr.BarPlot 常用参数速查
以本仓库 gradio/components/native_plot.py 的构造签名为准,除 every 外,gr.BarPlot 常用参数包括:
| 参数 | 类型 | 作用 |
|---|---|---|
value |
pandas DataFrame / 可调用对象 | 要展示的数据;传函数即实现"每次(定时/输入变化时)重查" |
x |
str | x 轴列名,可为数值、日期时间或字符串/类别 |
y |
str | y 轴列名,必须为数值列 |
color |
str | 用于按类别着色的列(系列) |
title |
str | 图表顶部标题(本教程即 "Prices"/"Inventory") |
x_title / y_title |
str | 自定义坐标轴标题,默认取 x/y 列名 |
y_aggregate |
"sum"/"mean"/"median"/"min"/"max"/"count" | 当 x_bin 被设置或 x 为类别时的聚合函数 |
x_bin |
number / "1h" 等 | 数值分箱或按 "s"/"m"/"h"/"d" 的时间分箱 |
sort |
"x"/"y"/"-x"/"-y" 或字符串列表 | 类别型 x 的排序方式 |
tooltip |
"axis"/"none"/"all" 或列名列表 | 悬停提示内容 |
height |
int | 图表高度(像素) |
buttons |
"fullscreen"/"export" 或 gr.Button() |
工具栏按钮:全屏查看或导出当前视图为 PNG |
every |
gr.Timer / float |
value 为函数时的定时重算间隔(秒) |
inputs |
组件或组件列表 | value 为函数时触发重算的输入组件 |
其中的"实时数据源 + 自动刷新"模式同样适用于 native_plot.py 中定义的 gr.LinePlot(折线图)与 gr.ScatterPlot(散点图)——三者共享同一套 NativePlot 实现,仅 mark 不同。
扩展:构建更丰富的实时仪表盘
本教程是可继续生长的起点,几点实用的扩展方向:
- 换数据、加图表:改用你的真实业务表,把
read_data中的表名与select字段替换即可;同一行内可并排放置更多gr.BarPlot/gr.LinePlot/gr.ScatterPlot,或配合gr.Row()、gr.Column()布局成多图面板。 - 联动筛选:给
gr.BarPlot增加inputs=gr.Dropdown(...)之类参数,当用户切换筛选条件时即重新查询,配合every实现"筛选维度 + 定时刷新"两种刷新并存。 - 用
every传浮点数:按 base.py 的实现,every=60会自动被包装成Timer(60),因此上面代码也可简写为gr.BarPlot(read_data, x="product_id", y="price", title="Prices", every=60);把间隔调小(如Timer(5))即可让刷新更加频繁。 - 打印/事件交互:设置
buttons=["fullscreen", "export"]让访问者一键全屏或导出 PNG。
如果想了解同一类组件更多用法,可继续阅读仓库内的 01_creating-plots.md(绘图基础)、02_time-plots.md(时间序列绘图)以及 04_connecting-to-a-database.md(SQLAlchemy 连接 SQLite/PostgreSQL/MySQL 的等价示例)。定时任务类应用的另一参考是 running-background-tasks.md。
结语
至此,你已经学会:向 Supabase 数据集写入数据,再把数据读取出来以柱状图呈现,并让仪表盘在一分钟内自动同步最新结果。更新 Supabase 中的数据后,Gradio 仪表盘会自动随之更新——尝试在示例中加入更多图表与可视化(或换用另一份数据集),把它扩展成功能更完整的实时监控面板吧。
回顾要点:① Supabase 建表并记录 Project URL 与 service_role 密钥;② supabase.create_client 建立连接后,用 insert/select 读写数据;③ 关键一行是把 DataFrame 生成函数直接传给 gr.BarPlot(...) 并设置 every=gr.Timer(60);④ 最后用 dashboard.queue().launch() 启动服务,让定时刷新事件可靠运行。这套"数据库查询函数 + Timer 自动重算"的模式,是 Gradio 中构建各类准实时数据可视化应用的通用范式。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00