01_界面工具的应用选择
明确需求优先级
工程数据分析项目通常以功能实现和数据可视化为主,界面只需满足基础交互。避免过度追求复杂UI效果,将精力集中在核心算法和数据处理上。
工具选型对比
- EasyGUI:适合超快速搭建基础对话框,零学习成本,但仅支持简单表单和消息框。
- PyQt5:功能全面但学习曲线陡峭,适合需要复杂桌面应用的场景。
- NiceGUI:基于Web的现代UI库,平衡了开发效率与交互体验,支持实时数据更新。
- PyWebview:轻量级浏览器封装,可将Web技术嵌入桌面应用,适合已有前端代码的情况。
| 特性 | PyQt/PySide | NiceGUI | pyWebview |
|---|---|---|---|
| 架构模式 | 原生Widget渲染 | Web技术+Python后端 | Web技术套壳原生窗口 |
| UI编写 | Qt Designer/QML | 纯Python声明式 | HTML/CSS/JS + Python后端 |
| 打包体积 | 较大(需带Qt库) | 中等(含FastAPI等) | 极小(系统原生WebView) |
| 适用场景 | 复杂商业桌面应用 | 快速Web化工具/数据分析 | 现代UI桌面应用/前端开发者转型 |
| 学习曲线 | 陡峭 | 平缓 | 平缓(需前端基础) |
推荐技术路径
NiceGUI作为首选方案,其特点包括:
- 直接生成可访问的Web界面,无需处理HTTP服务器
- 内置Matplotlib/Vue.js集成,完美适配数据可视化
- 声明式UI构建方式,代码量比PyQt减少50%以上
02_NiceGUI快速入门
NiceGui看起来像是bug一样的存在,它可以非常快速的让窗口界面和python的数据分析结合起来。我们稍微花点时间学习一下:
201_Label和按钮

202_基础组件
我们快速学习一下基础组件,包括label, html, button, input, checkbox, toggle, slider, number, select, radio等,这些组件直接用ui.就可以选取出来。
另外,按钮响应同样可以是函数,然后用def 去定义函数就可以了。应用起来真的是相当快速。

这里有几个以前没有注意到的知识点:
Quasar颜色、Tailwind颜色或CSS颜色

| #### Quasar 颜色(NiceGUI 原生支持) | #### Tailwind 颜色(通过 classes 使用) |
|---|---|
| # Quasar 预设颜色(最常用) colors = [ 'primary', # 主色调(默认蓝色) 'secondary', # 次要色(默认蓝绿色) 'accent', # 强调色(默认紫色) 'positive', # 成功(绿色) 'negative', # 错误(红色) 'info', # 信息(蓝色) 'warning', # 警告(橙色) 'dark', # 深色 'red', 'pink', 'purple', 'deep-purple', 'indigo', 'blue', 'light-blue', 'cyan', 'teal', 'green', 'light-green', 'lime', 'yellow', 'amber', 'orange', 'deep-orange', 'brown', 'grey', 'blue-grey' ] # 使用方式1:组件 color 参数 ui.button('成功', color='positive') ui.button('警告', color='warning') ui.icon('favorite', color='red', size='2em') # 使用方式2:CSS 类(背景色、文字色) ui.label('红色背景').classes('bg-red text-white') ui.label('蓝色文字').classes('text-blue') # 使用方式3:颜色深浅(-1 到 -14,数字越大越深/越浅) ui.label('浅红').classes('bg-red-3') ui.label('深红').classes('bg-red-10') ui.label('主色浅色').classes('bg-primary-2') | # Tailwind 颜色语法:{属性}-{颜色}-{明度} # 属性:bg(背景)、text(文字)、border(边框)、ring(轮廓) # 颜色:slate、gray、red、orange、amber、yellow、lime、green、 # emerald、teal、cyan、sky、blue、indigo、violet、purple、 # fuchsia、pink、rose # 明度:50、100、200、300、400、500、600、700、800、900、950 # 背景色示例 ui.label(' slate-100 背景').classes('bg-slate-100') ui.label(' emerald-500 背景').classes('bg-emerald-500 text-white') ui.label('渐变背景').classes('bg-gradient-to-r from-pink-500 to-violet-500 text-white') # 文字色示例 ui.label('蓝色文字').classes('text-blue-600') ui.label('灰色次要文字').classes('text-gray-500') # 边框色 ui.input('带色边框').classes('border-2 border-indigo-500') |
props()
.props()是 NiceGUI 中一个非常强大的方法,用于直接传递原始属性到底层的 Quasar 组件(Vue.js 组件)。
from nicegui import ui # 基本语法 element.props('属性名="值"') element.props('属性名') # 布尔属性(无需值) element.props('属性1="值1" 属性2="值2"') # 多个属性本质 :
.props()直接操作 Vue 组件的props,可以访问 Quasar 组件文档中定义的所有原生属性,这些属性可能没有封装到 NiceGUI 的 Python API 中。对比:NiceGUI 参数 vs .props()
from nicegui import ui # ========== 方式1:NiceGUI 封装参数(Pythonic)========== ui.button('点击', color='primary', icon='save', on_click=lambda: None) # 优点:类型安全、自动补全、Python 风格 # 缺点:只封装了常用属性 # ========== 方式2:.props() 原始属性(访问全部功能)========== ui.button('点击').props('no-caps loading percentage=50') # 优点:访问 Quasar 组件的全部能力 # 缺点:字符串形式,无类型检查,需查阅 Quasar 文档 # ========== 方式3:混合使用(推荐)========== ui.button('保存', icon='save', color='primary')\ .props('no-caps flat dense')\ .props('ripple=false') # 链式调用多个 props如何查找可用的 props?
# 方法1:查阅 Quasar 官方文档 # https://quasar.dev/vue-components # 方法2:在 NiceGUI 中查看元素类型 btn = ui.button('测试') print(type(btn)) # <class 'nicegui.elements.button.Button'> # 对应 Quasar 的 QBtn 组件 # 方法3:使用浏览器的开发者工具 # 右键检查元素 -> Vue 标签页查看 props
203_布局
1. 卡片式布局

# 常用宽度类对照表
widths = [
('w-16', '4rem', '64px'),
('w-24', '6rem', '96px'),
('w-32', '8rem', '128px'),
('w-48', '12rem', '192px'),
('w-64', '16rem', '256px'),
('w-80', '20rem', '320px'),
('w-96', '24rem', '384px'), # 你用的这个
]
# 更大的尺寸
# w-[100px] -> 任意值(Tailwind 任意值语法)
# w-1/2 -> 50%
# w-1/3 -> 33.333%
# w-full -> 100%
# w-screen -> 100vw
# w-auto -> auto
# w-fit -> fit-content
# w-min -> min-content
# w-max -> max-content
# 小卡片(手机尺寸)
with ui.card().classes('w-80'): # 320px
ui.label('小卡片').classes('text-h6')
# 中卡片(平板尺寸)- 你用的
with ui.card().classes('w-96'): # 384px
ui.label('中卡片').classes('text-h6')
# 大卡片(桌面尺寸)
with ui.card().classes('w-[480px]'): # 480px
ui.label('大卡片').classes('text-h6')
# 全宽响应式(最常用)
with ui.card().classes('w-full sm:w-96 md:w-[480px] lg:w-[600px]'):
ui.label('响应式卡片').classes('text-h6')
ui.label('手机全宽 → 平板 384px → 桌面 480px → 大屏 600px')
2. 行,列,Grid布局

3. 标签页布局
标签页应该跟PyQt中的Tab页是一样的,只要点击了页标签,下面的空间都是这一页的。


典型实现示例
这种方式用于查看dataframe的数据是非常有效的方法,只需要简单的ui.table.from_pandas()就搞定了!但是echart稍微有点麻烦,它不接受pandas,需要使用 .tolist() 将 Pandas Series 转换为 Python 列表。

03_pyWebView相关知识点梳理
HTML+CSS+JS+DOM 核心要点回顾
HTML 基础
- 结构标签:
<html>,<head>,<body>,<div>,<span> - 表单元素:
<input>,<select>,<button> - 语义化标签:
<header>,<section>,<article>
CSS 核心
- 盒模型:
margin,padding,border - 布局:
flexbox,grid - 响应式设计:
@media查询
JavaScript 基础
- 变量与作用域:
let,const,var - 函数:箭头函数、回调函数
- 事件处理:
addEventListener
DOM 操作
- 节点选择:
document.querySelector,getElementById - 动态修改:
innerHTML,classList - 事件委托:利用事件冒泡优化性能
Vue.js 学习路径
基础阶段
- 核心概念:
data,methods,computed,v-model - 指令:
v-for,v-if,v-bind,v-on - 组件化开发:单文件组件(
.vue)
进阶内容
- 状态管理:Vuex(适用于复杂状态)
- 路由:Vue Router(SPA 开发)
- 生命周期钩子:
created,mounted
案例:数据绑定与列表渲染
<template>
<div>
<input v-model="message" placeholder="输入内容">
<ul>
<li v-for="item in items" :key="item.id">{{ item.text }}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
message: '',
items: [{ id: 1, text: '示例项' }]
};
}
};
</script>
Axios 集成与数据交互
核心用法
- 发起请求:
axios.get(),axios.post() - 全局配置:
baseURL,headers - 拦截器:请求/响应拦截
案例:从 Python 后端获取数据
axios.get('/api/data')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error('请求失败:', error);
});
Python 后端示例(Flask)
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/api/data')
def get_data():
return jsonify({"data": [1, 2, 3]})
if __name__ == '__main__':
app.run()
融合 Python 数据分析
技术栈组合
- 后端:Python(Flask/Django)提供 API
- 前端:Vue.js 可视化数据
- 数据传输:Axios 异步请求
案例:图表展示(Vue + Canvas/Chart.js)
-
后端返回 JSON 格式数据(如 Pandas 处理后的结果)。
-
前端通过 Axios 获取数据并渲染图表。
// Vue 组件中
methods: {
fetchData() {
axios.get('/api/analytics')
.then(response => {
this.drawChart(response.data); // 使用 Canvas 或 Chart.js 绘图
});
}
}
学习资源推荐
- Vue 官方文档(含示例)
- Axios GitHub 仓库(查看配置示例)
- MDN Web Docs(巩固基础)
- 【跟AI老师学Pywebview】#1 开始上手-CSDN博客
- pywebview桌面程序开发(技术路线:前端+Python,全网独一份!!!!!!)-CSDN博客
04_pyWebView入门实践
AI助手给出的内容总是冰冷的没有感情,它不会基于个人已有的基础和特质,而人类需要有时间消化,反复以加深印象包括拓宽与其他知识点的连接,人类的学习必然遵守类似小游戏中周身旋转的武器的多少,多于敌人则全部转化为自身力量,否则只能被消耗。
401_pyWebView的Hello World
在很久之前学习HTML+CSS+JavaScript的时候,其实很多的工作中的便利小工具都在用它们在做,比如计算Lambda-空燃比-燃空比,计算轮胎型号-半径,百公里油耗与g的换算等。但是对于导入文件进行处理上总是受到各种各样的限制,无奈要通过python来读写数据文件转换成json再给到HTML文件进行显示。要不然就只能使用复杂的wxPython或PyQt进行界面设计。
或许有了pyWebView之后,一切就简单了?
先进行第一个程序的制作吧,由于结合了前端和后端,所以我们需要2个文件来完成:
- 先有一个.html文件(这个html文件原来是用浏览器打开的)
- 有一个.py文件 (现在用webview创建一个窗口打开 index.html,地址栏就没有了)

首先,create_window()这个函数,CTRL+点击到原文件webview/init.py,可以快速搞清楚
python
def create_window(
title: str, # 网页的标题(会覆盖.html的title)
url: str | callable | None = None, # html与url同时存在,html优先级高
html: str | None = None,
js_api: Any = None, # js_api - 在py文件中定义
width: int = 800, # 窗口的尺寸,默认800x600
height: int = 600,
402_网页与py的初次交互
从Hello World进行最简单的添加,同样是HTML文件和py文件只要有交互的地方,都要做相应的更改,这个对于某些人来说可能感觉很难受,这个跟之前用CodeBlock编写MFC的程序有点类似,资源文件上添加一个控件,要跟着改几个文件中的相关代码。但这些将来都不是个事,在AI编程的时代,只需要一句话就能让AI agent帮你完成~
- html文件:
- 添加按钮<button>Click me</button> -- 这个直接可以在html中点击弹出 hello world!
- 添加按钮To_python_print,onclick = "pywebview.api.python_print()" -- 即点击时通过pywebview中的api调取了py文件中的函数 (注意pywebview而不是webview,api而不是pyApi)
- py文件:
- 创建类 class pyApi, 定义一个函数python_print(),实现在python中打印
- 将这个类填到create_window的 js_api = pyApi() 属性中

综上,我们把html和py文件都进行了2处更改,就实现了在网页上点击按钮,然后到py文件中进行处理的功能。这就好像我们在pyQt界面上实现了一个按钮点击的槽函数这种最简单的应用。
403_在网页上显示py的返回值
上一个实例实现了最初级的交互响应,我们简单的迈进一步,让网页上显示py函数的返回值。当然这个功能无法在button οnclick="" 完成了,于是我们在html中需要用上<script>,而在py文件的函数中需要return
- html文件
- 界面添加一个用于显示的段落 <p id='response'> </p>,一个新按钮 show_ret
- 添加<script> function show_return(){ },
- 函数内a. 调用py的函数, b. 在段落中显示
- py文件
- 在pyApi中添加一个show_py_return函数

这里要注意,在JavaScript中,async和await是处理异步操作的一种方法。如果不使用,则很大可能返回的值是空的。
404_把文本或文件输入给py
前面已经完成:
- 用py打开一个html
- 在网页上点按钮到py中执行
- py返回值到网页中显示
接着我们来再前进一步,把网页上的数据信息传递给py进行数据分析后返回,我们先从一个简单的待办事项实例来看:
- html文件
- 界面设计:输入框(id=itemInput)+添加到python按钮(调用<script>中的addItem()函数,div(result)显示状态
- 异步操作,先把输入框的内容赋给title,调用api.add_item(title)
- 返回值(result)在div中进行显示
- py文件
- API中用成员变量self.items=[]记录json数据
- add_item(title)函数执行,把json数据append到self.items中,返回执行状态

405_运行额外的JS函数
很多时候,HTML文件中本身包含很多JS的函数,这些大多数在HTML中自己调用,但可能有的时候在py中也想调用它们,那么就用到evaluate_js()的方法。
- html文件中 - <script>中有个function test()
- py文件中 - 借用上面的python_print()函数,除了自己print外,使用win.evaluate_js("test()")调用Html中的test()函数

406_读取文件
对于pyWebview的用户来说,不推荐使用在HTML中<input>file的方式获取文件再处理,document.getElementById("fileInput").files[0] 得到的是 File(继承自 Blob)。在控制台里看起来像"一堆键值对",容易像 JSON,但本质是 浏览器里的文件句柄,只有元数据,没有磁盘路径。网页(包括 pywebview 里的 WebView)不允许 JS 拿到用户磁盘上的完整路径,只能拿到例如:name:文件名(如 a.txt),size、type、lastModified 等。如果采用这种方式,用户选中的文件只能在 前端 用 FileReader 读出文本(或 base64),再通过 pywebview.api 把 字符串 传给 Python,Python 只处理内容,不 open 磁盘路径。
对这种"不想在前端折腾读文件"的需求,用 create_file_dialog 从 Python 选文件通常更合适。
实现上就是:HTML 只负责触发 API,选文件和读写全在 Python。
- html文件:
- 创建一个按钮,οnclick=read_file2(),
- <script>中写funcion read_file2()函数,调用window.pywebview.api.read_file2()
- py文件
- 通过window.create_file_dialog() 打开文件对话框,模式为webview.FileDialog.OPEN
- 获取文件路径后用Open去读文件,返回文件的文本内容

6.x 推荐写法(用
FileDialog.LOAD)6.0 起用枚举替代旧常量,打开文件用
webview.FileDialog.LOAD(旧名OPEN_DIALOG已弃用但仍可能能用)。要点:
create_file_dialog要挂在「窗口对象」上,例如window.create_file_dialog(...),所以 API 类里需要能拿到创建好的那个window。
到这里,我们已经基本上搞明白pywebview的基本应用,可以比较轻松的通过HTML为界面而后端用python(numpy,pandas等)进行数据处理和分析了。