Python NiceGUI类组件异常排查及组件创建方式咨询
NiceGUI Todo List类组件异常问题及组件化方案咨询
问题描述
最近在用NiceGUI,把官方Todo List示例改成类组件形式后,单个列表运行正常,但创建第二个列表时出现异常:标记任务完成时其中一个列表无响应,必须增删任务才会更新,之后另一个列表又会停止响应。
我的实现代码
from nicegui import ui from dataclasses import dataclass from typing import List @dataclass class TodoItem: name: str done: bool = False class TodoListComponent: def __init__(self, title: str, items: List[TodoItem] = None): self.title = title if items: self.items = items else: self.items = [] def view(self): with ui.card().classes('w-80 items-stretch'): ui.label(self.title).classes('text-semibold test-2xl') self.on_change() add_input = ui.input('New item').classes('mx-12') add_input.on('keydown.enter', lambda: (self.add(add_input.value), add_input.set_value(''))) def add(self, name, done=False): self.items.append(TodoItem(name=name)) self.on_change.refresh() def remove(self, item): self.items.remove(item) self.on_change.refresh() @ui.refreshable def on_change(self): print(self.title, self.items) # 标记第一个列表的任务完成时,可能会打印第二个列表的信息 if not self.items: ui.label('List is empty.').classes('mx-auto') return done_items = sum(item.done for item in self.items) ui.linear_progress(done_items / len(self.items), show_value=False) with ui.row().classes('justify-center w-full'): ui.label(f'Completed {done_items}') ui.label(f'Remaining: {len(self.items) - done_items}') for item in self.items: with ui.row().classes('items-center'): ui.checkbox(value=item.done, on_change=self.on_change.refresh).bind_value(item, 'done') ui.input(value=item.name).classes('flex-grow').bind_value(item, 'name') ui.button(on_click=lambda item=item: self.remove(item), icon='delete')\ .props('flat fab-mini color=grey') todos = TodoListComponent('My Friday') todos.add('Order pizza', done=True) todos.add('New NiceGUI Release') todos.add('Clean the house') todos.add('Call mom') todos.view() todos_2 = TodoListComponent('My Monday') todos_2.add('Order pizza', done=True) todos_2.add('New NiceGUI Release') todos_2.add('Clean the house') todos_2.add('Call mom') todos_2.view() ui.run()
疑问
同时想咨询,能不能像JavaScript框架那样在NiceGUI里创建组件,或者Python里有其他替代方案?
解决方案
问题原因
异常的核心是@ui.refreshable装饰的on_change方法没有做实例级隔离:多个组件实例共享了refreshable函数的上下文,导致刷新操作混淆了不同实例的状态;另外checkbox的on_change回调直接绑定self.on_change.refresh,没有正确捕获当前实例的上下文,引发跨实例的状态更新混乱。
修正后的代码
from nicegui import ui from dataclasses import dataclass from typing import List @dataclass class TodoItem: name: str done: bool = False class TodoListComponent: def __init__(self, title: str, items: List[TodoItem] = None): self.title = title self.items = items if items else [] self.refreshable_container = None # 保存当前实例的refreshable容器引用 def view(self): with ui.card().classes('w-80 items-stretch'): ui.label(self.title).classes('text-semibold text-2xl') # 修正原代码的test-2xl拼写错误 self.refreshable_container = self.on_change() # 绑定当前实例的refreshable容器 add_input = ui.input('New item').classes('mx-12') add_input.on('keydown.enter', lambda: (self.add(add_input.value), add_input.set_value(''))) def add(self, name, done=False): self.items.append(TodoItem(name=name, done=done)) self.refreshable_container.refresh() def remove(self, item): self.items.remove(item) self.refreshable_container.refresh() @ui.refreshable def on_change(self): print(self.title, self.items) if not self.items: ui.label('List is empty.').classes('mx-auto') return done_items = sum(item.done for item in self.items) ui.linear_progress(done_items / len(self.items), show_value=False) with ui.row().classes('justify-center w-full'): ui.label(f'Completed {done_items}') ui.label(f'Remaining: {len(self.items) - done_items}') for item in self.items: with ui.row().classes('items-center'): # 用lambda捕获当前实例的refresh方法,避免上下文混淆 ui.checkbox(value=item.done, on_change=lambda _: self.refreshable_container.refresh()).bind_value(item, 'done') ui.input(value=item.name).classes('flex-grow').bind_value(item, 'name') ui.button(on_click=lambda item=item: self.remove(item), icon='delete')\ .props('flat fab-mini color=grey') todos = TodoListComponent('My Friday') todos.add('Order pizza', done=True) todos.add('New NiceGUI Release') todos.add('Clean the house') todos.add('Call mom') todos.view() todos_2 = TodoListComponent('My Monday') todos_2.add('Order pizza', done=True) todos_2.add('New NiceGUI Release') todos_2.add('Clean the house') todos_2.add('Call mom') todos_2.view() ui.run()
组件化方案及替代方案
NiceGUI组件化实现
- 你当前的类组件思路完全可行,核心是通过
@ui.refreshable隔离每个组件的渲染上下文,同时让每个实例持有自己的refreshable容器引用,避免状态混淆。 - 也可以通过
ui.element自定义原生组件,或组合现有组件封装成复用性更高的类/函数,实现类似Vue/React的组件模式。NiceGUI官方也推荐用类封装复杂组件,通过view方法渲染UI,配合refreshable机制实现状态更新。
- 你当前的类组件思路完全可行,核心是通过
Python替代方案
- PyWebIO:轻量级Web UI框架,支持函数式和类组件,适合快速构建交互式应用。
- Streamlit:专注数据可视化的框架,通过脚本式写法实现组件复用,适合数据分析场景。
- FastAPI + Vue/React:如果需要复杂前端交互,可用FastAPI做后端,配合JS框架开发前端,实现前后端分离。
- PyQt/PySide:桌面应用框架,支持面向对象的组件封装,适合桌面端GUI开发。
内容的提问来源于stack exchange,提问作者dudavik
相关产品推荐
相关产品推荐

