SkillAgentSearch skills...

Ex4nicegui

An extension library for nicegui. It has built-in responsive components and fully implements data-responsive interface programming.

Install / Use

/learn @CrystalWindSnake/Ex4nicegui
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

ex4nicegui

<div align="center">

简体中文| English

</div>

nicegui 做的扩展库。内置响应式组件,完全实现数据响应式界面编程。

todo-app

todo-app

查看更多示例


教程

头条文章-秒杀官方实现,python界面库,去掉90%事件代码的nicegui

微信公众号-秒杀官方实现,python界面库,去掉90%事件代码的nicegui

📦 安装

pip install ex4nicegui -U

入门

我们从一个简单的计数器应用开始,用户可以通过点击按钮让计数增加或减少。

counter

下面是完整代码:

from nicegui import ui
from ex4nicegui import rxui

# 数据状态代码
class Counter(rxui.ViewModel):
    count: int = 0

    def increment(self):
        self.count += 1

    def decrement(self):
        self.count -= 1

# 界面代码
counter = Counter()

with ui.row(align_items="center"):
    ui.button(icon="remove", on_click=counter.decrement)
    rxui.label(counter.count)
    ui.button(icon="add", on_click=counter.increment)


ui.run()

现在看更多细节。ex4nicegui 遵从数据驱动方式定义界面。状态数据定义应用程序中所有可以变化的数据。

下面是 Counter 状态数据定义:

class Counter(rxui.ViewModel):
    count: int = 0
  • 自定义类需要继承 rxui.ViewModel
  • 这里定义了一个变量 count,表示计数器的当前值,初始值为 0

接着,在类中定义一系列操作数据的方法:

def increment(self):
    self.count += 1

def decrement(self):
    self.count -= 1
  • 这些都是实例方法,可以修改 count 变量的值

然后,在界面代码中,实例化 Counter 的对象。

counter = Counter()

我们通过 rxui.label 组件绑定 count 变量。把操作数据的方法绑定到按钮点击事件上。

ui.button(icon="remove", on_click=counter.decrement)
rxui.label(counter.count)
ui.button(icon="add", on_click=counter.increment)
  • 我们需要使用 rxui 命名空间下的 label 组件,而不是 nicegui 命名空间下的 label 组件。
  • rxui.label 组件绑定 counter.count 变量,当 counter.count 变化时,rxui.label 组件自动更新。
  • ui.button 组件绑定 counter.decrementcounter.increment 方法,点击按钮时调用相应方法。

在复杂项目中,Counter 定义的代码可以放到单独的模块中,然后在界面代码中导入。

注意,当类变量名前面带有下划线时,数据状态不会自动更新。

class Counter(rxui.ViewModel):
    count: int = 0 # 响应式数据,能自动同步界面
    _count: int = 0 # 这里的下划线表示私有变量,不会自动同步界面


二次计算

接着前面的例子,我们再添加一个功能。当计数器的值小于 0 时,字体显示为红色,大于 0 时显示为绿色,否则显示为黑色。

# 数据状态代码
class Counter(rxui.ViewModel):
    count: int = 0

    def text_color(self):
        if self.count > 0:
            return "green"
        elif self.count < 0:
            return "red"
        else:
            return "black"

    def increment(self):
        self.count += 1

    def decrement(self):
        self.count -= 1

# 界面代码
counter = Counter()

with ui.row(align_items="center"):
    ui.button(icon="remove", on_click=counter.decrement)
    rxui.label(counter.count).bind_color(counter.text_color)
    ui.button(icon="add", on_click=counter.increment)

颜色值是依据计数器当前值计算得到的。属于二次计算。通过定义普通的实例函数即可。

def text_color(self):
    if self.count > 0:
        return "green"
    elif self.count < 0:
        return "red"
    else:
        return "black"

然后,通过 rxui.label 组件的 bind_color 方法绑定 text_color 方法,使得颜色值自动更新。

rxui.label(counter.count).bind_color(counter.text_color)

二次计算缓存

现在,我们在计数器下方使用文字,显示当前计数器的颜色文本值。

...
# 数据状态代码
class Counter(rxui.ViewModel):
    ...

# 界面代码
counter = Counter()

with ui.row(align_items="center"):
    ui.button(icon="remove", on_click=counter.decrement)
    rxui.label(counter.count).bind_color(counter.text_color)
    ui.button(icon="add", on_click=counter.increment)

rxui.label(lambda: f"当前计数器值为 {counter.count}, 颜色值为 {counter.text_color()}")
  • 当二次计算非常简单时,可以直接使用 lambda 表达式

上面的代码中,有两个地方使用了 counter.text_color 方法。当 counter.count 变化时,counter.text_color 会执行两次计算。第二次计算是多余的。

为了避免多余的计算,我们可以把 counter.text_color 缓存起来。

# 数据状态代码
class Counter(rxui.ViewModel):
    count: int = 0

    @rxui.cached_var
    def text_color(self):
        if self.count > 0:
            return "green"
        elif self.count < 0:
            return "red"
        else:
            return "black"

  • rxui.cached_var 装饰器可以把函数结果缓存起来,避免多余的计算。

列表

下面的示例,展示了如何使用列表。


class AppState(rxui.ViewModel):
    nums = []
    # nums = [1,2,3] ❌ 如果需要初始化,必须在 __init__ 中设置

    def __init__(self):
        super().__init__()
        self.nums = [1, 2, 3]

    def append(self):
        new_num = max(self.nums) + 1
        self.nums.append(new_num)

    def pop(self):
        self.nums.pop()

    def reverse(self):
        self.nums.reverse()

    def display_nums(self):
        return ", ".join(map(str, self.nums))


# 界面代码
state = AppState()

with ui.row(align_items="center"):
    ui.button("append", on_click=state.append)
    ui.button("pop", on_click=state.pop)
    ui.button("reverse", on_click=state.reverse)

rxui.label(state.display_nums)

如果你需要在定义列表时,初始化列表,建议在 __init__ 中设置。

class AppState(rxui.ViewModel):
    nums = []
    # nums = [1,2,3] ❌ 如果需要初始化,必须在 __init__ 中设置

    def __init__(self):
        super().__init__()
        self.nums = [1, 2, 3]

    ...

另一种方式是使用 rxui.list_var

class AppState(rxui.ViewModel):
    # nums = []
    # nums = [1,2,3] ❌ 如果需要初始化,必须在 __init__ 中设置
    nums = rxui.list_var(lambda: [1, 2, 3])

    ...
  • rxui.list_var 参数是一个返回列表的函数

列表循环

定义列表后,我们可以用 effect_refreshable.on 装饰器,在界面中展示列表数据。

下面的例子中,界面会动态展示下拉框选中的图标

from ex4nicegui import rxui, effect_refreshable


class AppState(rxui.ViewModel):
    icons = []
    _option_icons = ["font_download", "warning", "format_size", "print"]


state = AppState()

# 界面代码
with ui.row(align_items="center"):

    @effect_refreshable.on(state.icons)
    def _():
        for icon in state.icons:
            ui.icon(icon, size="2rem")


rxui.select(state._option_icons, value=state.icons, multiple=True)

其中,@effect_refreshable.on(state.icons) 明确指定了依赖关系。当 state.icons 变化时,_ 函数会重新执行。

@effect_refreshable.on(state.icons)
def _():
    # 这里的代码会在 state.icons 变化时重新执行
    ...

注意,每次执行,里面的内容都会被清除。这是数据驱动版本的 ui.refreshable

原则上,可以不通过 .on 指定监控的数据,只要函数中使用到的"响应式数据",都会自动监控

@effect_refreshable # 没有使用 .on(state.icons)
def _():
    # 这里读取了 state.icons,因此会自动监控
    for icon in state.icons:
        ui.icon(icon, size="2rem")

建议总是通过 .on 指定依赖关系,避免预料之外的刷新


数据持久化

ViewModel 使用代理对象创建响应式数据,当需要保存数据时,可以使用 rxui.ViewModel.to_value 转换成普通数据.

下面的例子,点击按钮将显示 my_app 的状态数据字典。

from nicegui import ui
from ex4nicegui import rxui


class MyApp(rxui.ViewModel):
    a = 0
    sign = "+"
    b = 0

    def show_data(self):
        # >> {"a": 0, "sign": '+, "b": 0}
        return rxui.ViewModel.to_value(self)

    def show_a(self):
        # >> 0
        return rxui.ViewModel.to_value(self.a)

my_app = MyApp()

rxui.number(value=my_app.a, min=0, max=10)
rxui.radio(["+", "-", "*", "/"], value=my_app.sign)
rxui.number(value=my_app.b, min=0, max=10)

ui.button("show data", on_click=lambda: ui.notify(my_app.show_data()))

结合 rxui.ViewModel.on_refs_changed ,可以在数据变化时,自动保存数据到本地。

from nicegui import ui
from ex4nicegui import rxui
from pathlib import Path
import json


class MyApp(rxui.ViewModel):
    a = 0
    sign = "+"
    b = 0

    _json_path = Path(__file__).parent / "data.json"

    def __init__(self):
        super().__init__()

        @rxui.ViewModel.on_refs_changed(self)
        def _():
            # a, sign, b 任意一个值变化时,自动保存到本地
            self._json_path.write_text(json.dumps(self.show_data()))

    def show_data(self):
        return rxui.ViewModel.to_value(self)
...



apis

ViewModel

v0.7.0 版本中,引入 ViewModel 类,用于管理一组响应式数据。

下面是一个简单的计算器示例:

  1. 当用户修改数值输入框或符号选择框,右侧会自动显示计算结果
  2. 当结果小于 0 时,结果显示为红色,否则为黑色
from ex4nicegui import rxui

class Calculator(rxui.ViewModel):
    num1 = 0
    sign = "+"
    num2 = 0

    @rxui.cached_var
    def result(self):
        # 当 num1,sign,num2 任意一个值发生变化时,result 也会重新计算
        return eval(f"{self.num1}{self.sign}{self.num2}")

# 每个对象拥有独立的数据
calc = Calculator()

with ui.row(align_items="center"):
    rxui.number(value=calc.num1, label="Number 1")
    rxui.select(value=calc.sign, options=["+", "-", "*", "/"], label="Sign")
    rxui.number(value=calc.num2, label="Number 2")
    ui.label("=")
    rxui.label(calc.result).bind_color(
        lambda: "red" if calc.r

Related Skills

View on GitHub
GitHub Stars212
CategoryDevelopment
Updated7d ago
Forks13

Languages

Python

Security Score

100/100

Audited on Apr 1, 2026

No findings