图:
官方例 代码:
#!/usr/bin/env python3
import uvicorn
from fastapi import FastAPI
from nicegui import app, ui
# This example deliberately creates a separate FastAPI app and runs NiceGUI on top of it using `ui.run_with`.
# Please note that the `app` object from NiceGUI is also a FastAPI app.
# Often it is easier to stick to `ui.run` and use the `@app.get` etc. decorators to add normal FastAPI endpoints.
fastapi_app = FastAPI()
@fastapi_app.get('/')
def get_root():
return {'message': 'Hello, FastAPI! Browse to /gui to see the NiceGUI app.'}
@ui.page('/')
def show():
ui.label('Hello, NiceGUI!')
# NOTE dark mode will be persistent for each user across tabs and server restarts
ui.dark_mode().bind_value(app.storage.user, 'dark_mode')
ui.checkbox('dark mode').bind_value(app.storage.user, 'dark_mode')
ui.run_with(
fastapi_app,
mount_path='/gui', # NOTE this can be omitted if you want the paths passed to @ui.page to be at the root
storage_secret='pick your private secret here', # NOTE setting a secret is optional but allows for persistent storage per user
)
if __name__ == '__main__':
uvicorn.run('main:fastapi_app', log_level='info', reload=True)
变化下 代码:
#!/usr/bin/env python3
import uvicorn
from fastapi import FastAPI
from nicegui import app, ui
import httpx
# 1. 创建FastAPI应用(官方方式)
fastapi_app = FastAPI(title="Hello NiceGUI 3.x 集成")
# 2. 添加纯FastAPI端点(不经过NiceGUI)
@fastapi_app.get("/api/hello")
async def hello_api(name: str = "Hello"):
"""纯FastAPI端点,返回JSON数据"""
return {"message": f"你好, {name}!", "status": "success", "version": "3.x"}
@fastapi_app.get("/api/status")
async def status_api():
"""系统状态检查"""
return {
"status": "运行中",
"service": "NiceGUI + FastAPI",
"company": "Hello"
}
@fastapi_app.post("/api/echo")
async def echo_api(data: dict):
"""回声测试"""
return {"echo": data, "timestamp": "2026-01-01"}
# 3. NiceGUI页面定义
@ui.page('/')
def index():
"""主页面 - 仪表板"""
ui.label('Hello NiceGUI 3.x 集成仪表板').style('''
font-size: 24px;
color: #ff6a00;
font-weight: bold;
margin-bottom: 20px;
''')
# 状态卡片
with ui.card().style('width: 100%; padding: 15px; background: #fff3e0;'):
ui.label('系统状态').style('font-weight: bold; color: #ff6a00;')
status_label = ui.label('等待检测...').style('margin-top: 5px;')
async def check_status():
try:
async with httpx.AsyncClient() as client:
response = await client.get('http://localhost:8000/api/status')
data = response.json()
status_label.text = f"✓ {data['status']} - {data['service']}"
status_label.style('color: #28a745;')
ui.notify('系统正常', type='positive')
except Exception as e:
status_label.text = f"✗ 错误: {str(e)}"
status_label.style('color: #dc3545;')
ui.notify('连接失败', type='negative')
ui.button('检查状态', on_click=check_status).style('margin-top: 10px;')
# 功能导航
ui.label('功能导航').style('font-size: 18px; margin-top: 20px; margin-bottom: 10px; font-weight: bold;')
with ui.row():
ui.link('API测试', '/api-test').style('''
padding: 12px 24px;
background: #ff6a00;
color: white;
text-decoration: none;
border-radius: 6px;
font-weight: bold;
''')
ui.link('实时监控', '/monitor').style('''
padding: 12px 24px;
background: #0066cc;
color: white;
text-decoration: none;
border-radius: 6px;
font-weight: bold;
''')
ui.link('数据可视化', '/visual').style('''
padding: 12px 24px;
background: #28a745;
color: white;
text-decoration: none;
border-radius: 6px;
font-weight: bold;
''')
@ui.page('/api-test')
def api_test():
"""API测试页面"""
ui.label('API 端点测试').style('font-size: 20px; color: #0066cc;')
ui.link('← 返回仪表板', '/').style('color: #666; margin-bottom: 15px; display: block;')
# 测试结果展示区
result = ui.label('').style('''
padding: 15px;
background: #f8f9fa;
border-radius: 6px;
min-height: 60px;
margin: 10px 0;
font-family: monospace;
''')
# 输入框
name_input = ui.input('名称').style('margin: 10px 0;')
# 测试按钮组
async def test_hello():
try:
async with httpx.AsyncClient() as client:
name = name_input.value or "Hello"
response = await client.get(f'http://localhost:8000/api/hello?name={name}')
result.text = f"GET /api/hello\n{response.json()}"
result.style('background: #d4edda; color: #155724;')
except Exception as e:
result.text = f"错误: {str(e)}"
result.style('background: #f8d7da; color: #721c24;')
async def test_status():
try:
async with httpx.AsyncClient() as client:
response = await client.get('http://localhost:8000/api/status')
result.text = f"GET /api/status\n{response.json()}"
result.style('background: #d4edda; color: #155724;')
except Exception as e:
result.text = f"错误: {str(e)}"
result.style('background: #f8d7da; color: #721c24;')
async def test_echo():
try:
async with httpx.AsyncClient() as client:
response = await client.post('http://localhost:8000/api/echo',
json={"test": "data", "user": "Hello"})
result.text = f"POST /api/echo\n{response.json()}"
result.style('background: #d4edda; color: #155724;')
except Exception as e:
result.text = f"错误: {str(e)}"
result.style('background: #f8d7da; color: #721c24;')
with ui.row():
ui.button('测试GET /api/hello', on_click=test_hello)
ui.button('测试GET /api/status', on_click=test_status)
ui.button('测试POST /api/echo', on_click=test_echo)
@ui.page('/monitor')
def monitor():
"""实时监控页面"""
ui.label('实时监控').style('font-size: 20px; color: #0066cc;')
ui.link('← 返回仪表板', '/').style('color: #666; margin-bottom: 15px; display: block;')
# 监控指标
with ui.row():
metric1 = ui.label('CPU: --').style('font-size: 18px; color: #ff6a00;')
metric2 = ui.label('内存: --').style('font-size: 18px; color: #0066cc;')
metric3 = ui.label('请求: 0').style('font-size: 18px; color: #28a745;')
status_log = ui.log().style('height: 150px; background: #f8f9fa; margin-top: 10px; padding: 10px;')
async def simulate_monitoring():
import asyncio
import random
status_log.push('🚀 开始监控...')
for i in range(1, 11):
cpu = random.randint(20, 80)
mem = random.randint(30, 90)
req = i * 10
metric1.text = f'CPU: {cpu}%'
metric2.text = f'内存: {mem}%'
metric3.text = f'请求: {req}'
status_log.push(f'[{i}] CPU:{cpu}% Mem:{mem}% Req:{req}')
await asyncio.sleep(1)
status_log.push('✅ 监控完成')
ui.notify('监控周期结束', type='positive')
ui.button('开始模拟监控', on_click=simulate_monitoring).style('margin-top: 10px;')
@ui.page('/visual')
def visual():
"""数据可视化页面"""
ui.label('数据可视化').style('font-size: 20px; color: #28a745;')
ui.link('← 返回仪表板', '/').style('color: #666; margin-bottom: 15px; display: block;')
# 模拟图表数据
with ui.row():
value_label = ui.label('当前值: 0').style('font-size: 24px; font-weight: bold; color: #ff6a00;')
# 进度条
progress = ui.linear_progress(value=0).style('margin: 10px 0; width: 100%;')
# 数据列表
data_list = ui.label('').style('''
padding: 10px;
background: #f8f9fa;
border-radius: 6px;
min-height: 100px;
font-family: monospace;
white-space: pre-wrap;
''')
async def generate_data():
import asyncio
import random
import time
data = []
for i in range(1, 6):
value = random.randint(10, 100)
timestamp = time.strftime("%H:%M:%S")
value_label.text = f'当前值: {value}'
progress.set_value(i / 5)
data.append(f"[{timestamp}] 数据点 #{i}: {value}")
data_list.text = "\n".join(data)
await asyncio.sleep(0.8)
ui.notify('数据生成完成', type='positive')
progress.set_value(1.0)
ui.button('生成数据序列', on_click=generate_data).style('margin-top: 10px;')
# 4. 使用ui.run_with集成到FastAPI(官方推荐方式)
ui.run_with(
fastapi_app,
mount_path='/', # NiceGUI挂载在根路径
storage_secret='mi_mo_secret_key_2026', # 会话存储密钥
# 可选:自定义主题
# dark=True, # 默认暗色模式
)
# 5. 启动服务器(关键:移除 if __name__ 保护)
# 或改为:
if __name__ in {"__main__", "__mp_main__"}:
print("=" * 50)
print("🚀 Hello NiceGUI 3.x 集成服务启动")
print("=" * 50)
print("访问: http://localhost:8000")
print("API文档: http://localhost:8000/docs")
print("NiceGUI页面: http://localhost:8000")
print("=" * 50)
uvicorn.run('main:fastapi_app', host="0.0.0.0", port=8000, log_level="info", reload=False)
运行方式
# 1. 安装依赖
pip install fastapi nicegui uvicorn httpx
# 2. 保存为 main.py
# 3. 运行(关键:直接运行,不要加 if __name__ 保护)
python main.py
核心要点总结
- 1.
ui.run_with():将 NiceGUI 集成到 FastAPI - 2.
mount_path='/':NiceGUI 在根路径 -
@fastapi_app.get():添加纯 API 端点
-
@ui.page():定义 GUI 页面
- 5.
uvicorn.run():启动整个应用 - 6.移除
if __name__或改为if __name__ in {"__main__", "__mp_main__"}
访问地址
- 主界面:http://localhost:8000
- API文档:http://localhost:8000/docs
- API测试:http://localhost:8000/api-test
- 实时监控:http://localhost:8000/monitor
这个方案完全符合官方推荐,同时实现了单端口统一访问!