Skip to content

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

License

Notifications You must be signed in to change notification settings

CrystalWindSnake/ex4nicegui

Repository files navigation

ex4nicegui

ENGLISH README

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

教程

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

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

📦 安装

pip install ex4nicegui -U

示例项目


🦄 使用

from nicegui import ui
from ex4nicegui import ref_computed, effect, to_ref
from ex4nicegui.reactive import rxui

# 定义响应式数据
r_input = to_ref("")

# 按照 nicegui 使用方式传入响应式数据即可
rxui.input(value=r_input)
rxui.label(r_input)

ui.run()

提供 echarts 图表组件

from nicegui import ui
from ex4nicegui import ref_computed, effect, to_ref
from ex4nicegui.reactive import rxui

r_input = to_ref("")

# ref_computed 创建只读响应式变量
# 函数中使用任意其他响应式变量,会自动关联
@ref_computed
def cp_echarts_opts():
    return {
        "title": {"text": r_input.value}, #字典中使用任意响应式变量,通过 .value 获取值
        "xAxis": {
            "type": "category",
            "data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
        },
        "yAxis": {"type": "value"},
        "series": [
            {
                "data": [120, 200, 150, 80, 70, 110, 130],
                "type": "bar",
                "showBackground": True,
                "backgroundStyle": {"color": "rgba(180, 180, 180, 0.2)"},
            }
        ],
    }

input = rxui.input("输入内容,图表标题会同步", value=r_input)
# 通过响应式组件对象的 element 属性,获取原生 nicegui 组件对象
input.element.classes("w-full")

rxui.echarts(cp_echarts_opts)

ui.run()

echarts 图表鼠标事件

on 函数参数 event_name 以及 query 使用,查看echarts 事件中文文档

以下例子绑定鼠标单击事件

from nicegui import ui
from ex4nicegui.reactive import rxui

opts = {
    "xAxis": {"type": "value", "boundaryGap": [0, 0.01]},
    "yAxis": {
        "type": "category",
        "data": ["Brazil", "Indonesia", "USA", "India", "China", "World"],
    },
    "series": [
        {
            "name": "first",
            "type": "bar",
            "data": [18203, 23489, 29034, 104970, 131744, 630230],
        },
        {
            "name": "second",
            "type": "bar",
            "data": [19325, 23438, 31000, 121594, 134141, 681807],
        },
    ],
}

bar = rxui.echarts(opts)

def on_click(e: rxui.echarts.EChartsMouseEventArguments):
    ui.notify(f"on_click:{e.seriesName}:{e.name}:{e.value}")


bar.on("click", on_click)

以下例子只针对指定系列触发鼠标划过事件

from nicegui import ui
from ex4nicegui.reactive import rxui

opts = {
    "xAxis": {"type": "value", "boundaryGap": [0, 0.01]},
    "yAxis": {
        "type": "category",
        "data": ["Brazil", "Indonesia", "USA", "India", "China", "World"],
    },
    "series": [
        {
            "name": "first",
            "type": "bar",
            "data": [18203, 23489, 29034, 104970, 131744, 630230],
        },
        {
            "name": "second",
            "type": "bar",
            "data": [19325, 23438, 31000, 121594, 134141, 681807],
        },
    ],
}

bar = rxui.echarts(opts)

def on_first_series_mouseover(e: rxui.echarts.EChartsMouseEventArguments):
    ui.notify(f"on_first_series_mouseover:{e.seriesName}:{e.name}:{e.value}")


bar.on("mouseover", on_first_series_mouseover, query={"seriesName": "first"})

ui.run()

响应式

from ex4nicegui import (
    to_ref,
    ref_computed,
    on,
    effect,
    effect_refreshable,
    batch,
    event_batch,
    deep_ref,
    async_computed
)

常用 to_ref,deep_ref,effect,ref_computed,on,async_computed


to_ref

定义响应式对象,通过 .value 读写

a = to_ref(1)
b = to_ref("text")

a.value =2
b.value = 'new text'

print(a.value)

当值为复杂对象时,默认不会保持嵌套对象的响应性。

a = to_ref([1,2])

@effect
def _():
    print('len:',len(a.value))

# 不会触发 effect
a.value.append(10)

# 整个替换则会触发
a.value = [1,2,10]

参数 is_deep 设置为 True 时,能得到深度响应能力

a = to_ref([1,2],is_deep=True)

@effect
def _():
    print('len:',len(a.value))

# print 3
a.value.append(10)

deep_ref 等价于 is_deep 设置为 True 时的 to_ref


deep_ref

等价于 is_deep 设置为 True 时的 to_ref

当数据源为列表、字典或自定义类时,特别有用。通过 .value 获取的对象为代理对象

data = [1,2,3]
data_ref = deep_ref(data)

assert data_ref.value is not data

通过 to_raw 可以获取原始对象

from ex4nicegui import to_raw, deep_ref

data = [1, 2, 3]
data_ref = deep_ref(data)

assert data_ref.value is not data
assert to_raw(data_ref.value) is data

effect

接受一个函数,自动监控函数中使用到的响应式对象变化,从而自动执行函数

a = to_ref(1)
b = to_ref("text")


@effect
def auto_run_when_ref_value():
    print(f"a:{a.value}")


def change_value():
    a.value = 2
    b.value = "new text"


ui.button("change", on_click=change_value)

首次执行 effect ,函数auto_run_when_ref_value将被执行一次.之后点击按钮,改变 a 的值(通过 a.value),函数auto_run_when_ref_value再次执行

切忌把大量数据处理逻辑分散在多个 oneffect 中,oneffect 中应该大部分为界面操作逻辑,而非响应式数据处理逻辑


ref_computed

effect 具备一样的功能,ref_computed 还能从函数中返回结果。一般用于从 to_ref 中进行二次计算

a = to_ref(1)
a_square = ref_computed(lambda: a.value * 2)


@effect
def effect1():
    print(f"a_square:{a_square.value}")


def change_value():
    a.value = 2


ui.button("change", on_click=change_value)

点击按钮后,a.value 值被修改,从而触发 a_square 重新计算.由于 effect1 中读取了 a_square 的值,从而触发 effect1 执行

ref_computed 是只读的 to_ref

如果你更喜欢通过类组织代码,ref_computed 同样支持作用到实例方法上

class MyState:
    def __init__(self) -> None:
        self.r_text = to_ref("")

    @ref_computed
    def post_text(self):
        return self.r_text.value + "post"

state = MyState()

rxui.input(value=state.r_text)
rxui.label(state.post_text)

async_computed

二次计算中需要使用异步函数时,使用 async_computed

# 模拟长时间执行的异步函数
async def long_time_query(input: str):
    await asyncio.sleep(2)
    num = random.randint(20, 100)
    return f"query result[{input=}]:{num=}"


search = to_ref("")
evaluating = to_ref(False)

@async_computed(search, evaluating=evaluating, init="")
async def search_result():
    return await long_time_query(search.value)

rxui.lazy_input(value=search)

rxui.label(
    lambda: "查询中" if evaluating.value else "上方输入框输入内容并回车搜索"
)
rxui.label(search_result)
  • async_computed 第一个参数必须明确指定需要监控的响应式数据. 使用列表可以同时指定多个响应式数据
  • 参数 evaluating 为 bool 类型的响应式数据,当异步函数执行中,此变量值为 True,计算结束后为 False
  • 参数 init 指定初始结果

on

类似 effect 的功能,但是 on 需要明确指定监控的响应式对象

a1 = to_ref(1)
a2 = to_ref(10)
b = to_ref("text")


@on(a1)
def watch_a1_only():
    print(f"watch_a1_only ... a1:{a1.value},a2:{a2.value}")


@on([a1, b], onchanges=True)
def watch_a1_and_b():
    print(f"watch_a1_and_b ... a1:{a1.value},a2:{a2.value},b:{b.value}")


def change_a1():
    a1.value += 1
    ui.notify("change_a1")


ui.button("change a1", on_click=change_a1)


def change_a2():
    a2.value += 1
    ui.notify("change_a2")


ui.button("change a2", on_click=change_a2)


def change_b():
    b.value += "x"
    ui.notify("change_b")


ui.button("change b", on_click=change_b)
  • 参数 onchanges 为 True 时(默认值为 False),指定的函数不会在绑定时执行

切忌把大量数据处理逻辑分散在多个 oneffect 中,oneffect 中应该大部分为界面操作逻辑,而非响应式数据处理逻辑


new_scope

默认情况下,所有检测函数在客户端连接断开时自动销毁。如果需要更细粒度的控制,可以使用 new_scope

from nicegui import ui
from ex4nicegui import rxui, to_ref, effect, new_scope

a = to_ref(0.0)

scope1 = new_scope()

@scope1.run
def _():
    @effect
    def _():
        print(f"scope 1:{a.value}")


rxui.number(value=a)
rxui.button("dispose scope 1", on_click=scope1.dispose)

组件功能

vmodel

在表单输入元素或组件上创建双向绑定。

简单值类型的 ref 默认支持双向绑定

from ex4nicegui import rxui, to_ref, deep_ref

data = to_ref("init")

rxui.label(lambda: f"{data.value=}")
# 默认就是双向绑定
rxui.input(value=data)
  • 简单值类型一般是 str,int 等不可变值类型

当使用复杂数据结构时,会使用 deep_ref 保持嵌套值的响应性

data = deep_ref({"a": 1, "b": [1, 2, 3, 4]})

rxui.label(lambda: f"{data.value=!s}")

# 当前版本没有任何绑定效果.或许未来的版本可以解决
rxui.input(value=data.value["a"])

# 只读绑定.其他途径修改了 `data.value["a"]` ,此输入框会同步,但反过来不行
rxui.input(value=lambda: data.value["a"])

# 要使用 vmodel 才能双向绑定
rxui.input(value=rxui.vmodel(data.value["a"]))
  • 第一个输入框将完全失去响应性,因为代码等价于直接传入一个数值1
  • 第二个输入框由于使用函数,将得到读取响应性(第三个输入框输入值,将得到同步)
  • 第三个输入框,使用 rxui.vmodel 包裹,即可实现双向绑定

多数在配合 vfor 时使用 vmodel,可参考 todo list 案例

vfor

基于列表响应式数据,渲染列表组件。每项组件按需更新。数据项支持字典或任意类型对象

from nicegui import ui
from ex4nicegui.reactive import rxui
from ex4nicegui import deep_ref, ref_computed
from typing import Dict

# refs
items = deep_ref(
    [
        {"id": 1, "message": "foo", "done": False},
        {"id": 2, "message": "bar", "done": True},
    ]
)

# ref_computeds
@ref_computed
def done_count_info():
    return f"done count:{sum(item['done'] for item in items.value)}"

# method
def check():
    for item in items.value:
        item["done"] = not item["done"]


# ui
rxui.label(done_count_info)
ui.button("check", on_click=check)


@rxui.vfor(items,key='id')
def _(store: rxui.VforStore[Dict]):
    # 函数中构建每一行数据的界面
    item = store.get()  # 通过 store.get 获取对应行的响应式对象(相当于每行的数据 to_ref(...))
    mes = rxui.vmodel(item.value['message']) # 复杂结构默认没有双向绑定,需要使用 `vmodel`

    # 输入框输入内容,可以看到单选框的标题同步变化
    with ui.card():
        with ui.row():
            rxui.input(value=mes)
            rxui.label(lambda: f"{mes.value=!s}")
        rxui.checkbox(text=mes, value=rxui.vmodel(item.value['done']))
  • rxui.vfor 装饰器到自定义函数
    • 第一个参数传入响应式列表。列表中每一项可以是字典或其他对象(dataclasses 等等)
    • 第二个参数 key: 为了可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你可以为每个元素对应的块提供一个唯一的 key 。默认情况使用列表元素索引。
  • 自定义函数带有一个参数。通过 store.get 可以获取当前行的响应式对象

vfor 渲染的项目,只有在新增数据时,才会创建


绑定类名

所有的组件类提供 bind_classes 用于绑定 class,支持三种不同的数据结构。

绑定字典

bg_color = to_ref(False)
has_error = to_ref(False)

rxui.label("test").bind_classes({"bg-blue": bg_color, "text-red": has_error})

rxui.switch("bg_color", value=bg_color)
rxui.switch("has_error", value=has_error)

字典键值为类名,对应值为 bool 的响应式变量。当响应式值为 True,类名应用到组件 class


绑定返回值为字典的响应式变量

bg_color = to_ref(False)
has_error = to_ref(False)

class_obj = ref_computed(
    lambda: {"bg-blue": bg_color.value, "text-red": has_error.value}
)

rxui.switch("bg_color", value=bg_color)
rxui.switch("has_error", value=has_error)
rxui.label("bind to ref_computed").bind_classes(class_obj)
# or direct function passing
rxui.label("bind to ref_computed").bind_classes(
    lambda: {"bg-blue": bg_color.value, "text-red": has_error.value}
)

绑定为列表

bg_color = to_ref("red")
bg_color_class = ref_computed(lambda: f"bg-{bg_color.value}")

text_color = to_ref("green")
text_color_class = ref_computed(lambda: f"text-{text_color.value}")

rxui.select(["red", "green", "yellow"], label="bg color", value=bg_color)
rxui.select(["red", "green", "yellow"], label="text color", value=text_color)

rxui.label("binding to arrays").bind_classes([bg_color_class, text_color_class])

列表中每个元素为返回类名的响应式变量


bind-style

from nicegui import ui
from ex4nicegui.reactive import rxui
from ex4nicegui.utils.signals import to_ref


bg_color = to_ref("blue")
text_color = to_ref("red")

rxui.label("test").bind_style(
    {
        "background-color": bg_color,
        "color": text_color,
    }
)

rxui.select(["blue", "green", "yellow"], label="bg color", value=bg_color)
rxui.select(["red", "green", "yellow"], label="text color", value=text_color)

bind_style 传入字典,key 为样式名字,value 为样式值,响应式字符串


bind_prop

绑定单个属性

label = to_ref("hello")

rxui.button("").bind_prop("label", label)
# 允许使用函数
rxui.button("").bind_prop(
    "label", lambda: f"{label.value} world"
)

rxui.input(value=label)

rxui.echarts

使用 echarts 制作图表


rxui.echarts.from_javascript

从 javascript 代码创建 echart

from pathlib import Path

rxui.echarts.from_javascript(Path("code.js"))
# or
rxui.echarts.from_javascript(
    """
(myChart) => {

    option = {
        xAxis: {
            type: 'category',
            data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
        },
        yAxis: {
            type: 'value'
        },
        series: [
            {
                data: [120, 200, 150, 80, 70, 110, 130],
                type: 'bar'
            }
        ]
    };

    myChart.setOption(option);
}
"""
)
  • 函数第一个参数为 echart 实例对象.你需要在函数中通过 setOption 完成图表配置

函数也有第二个参数,为 echarts 全局对象,你可以通过 echarts.registerMap 注册地图。

rxui.echarts.from_javascript(
"""
(chart,echarts) =>{

    fetch('https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json')
    .then(response => response.json())
    .then(data => {
            echarts.registerMap('test_map', data);

            chart.setOption({
                geo: {
                    map: 'test_map',
                    roam: true,
                },
                tooltip: {},
                legend: {},
                series: [],
            });
    });
}
"""
)

rxui.echarts.register_map

注册地图.

rxui.echarts.register_map(
    "china", "https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json"
)

rxui.echarts(
    {
        "geo": {
            "map": "china",
            "roam": True,
        },
        "tooltip": {},
        "legend": {},
        "series": [],
    }
)
  • 参数 map_name 为自定义的地图名字。注意在图表配置中 map 必需对应注册的名字
  • 参数 src 为有效的地图数据网络链接。

如果是 svg 数据,需要设置参数 type="svg"

rxui.echarts.register_map("svg-rect", "/test/svg", type="svg")

你也可以直接提供本地地图数据的json文件路径对象(Path)

from pathlib import Path

rxui.echarts.register_map(
    "china", Path("map-data.json")
)

gsap

js 动画库. gsap文档

from nicegui import ui
from ex4nicegui import gsap

gsap.from_

设置起始属性,动画将从设置的属性过渡到原始位置

ui.label("test from").classes("target")
gsap.from_(".target", {"x": 50,'duration':1})

画面加载后,文本起始位置在往右偏移 50px 处,在 1秒 内移动到原始位置上

  • 参数 targets 为 css 选择器
  • 参数 vars 为属性值,具体参考 gsap 文档

gsap.to

设置结束属性,动画将从原始属性过渡到设置的属性

ui.label("test to").classes("target")
gsap.to(".target", {"x": 50,'duration':1})

画面加载后,文本在 1秒 内,从原始位置往后移动 50px

  • 参数 targets 为 css 选择器
  • 参数 vars 为属性值,具体参考 gsap 文档

gsap.run_script

通过编写 js 设置动画

gsap.run_script(
            r"""function setGsap(gsap) {
    gsap.to('.target',{"duration": 0.3,y:60})
}
""")
  • 参数 script 可以为文本或 js 后缀的文件 Path
  • 定义的 js 函数名字并不影响运行,第一个参数为 gsap 对象

tab_panels

相比较于 nicegui.ui.tab_panels , rxui.tab_panels 没有参数 tabs。在数据响应式机制下,tabstab_panels 联动只需要通过参数 value 即可。

from nicegui import ui
from ex4nicegui import rxui, to_ref

names = ["Tab 1", "Tab 2", "Tab 3"]
current_tab = to_ref(names[0])

with rxui.tabs(current_tab):
    for name in names:
        rxui.tab(name)

with rxui.tab_panels(current_tab):
    for name in names:
        with rxui.tab_panel(name):
            ui.label(f"Content of {name}")

这是因为,数据响应机制下,组件联动是通过中间数据层(to_ref)实现的。因此,tab_panels 可以与其他组件联动(只需要保证使用同样的 ref 对象即可)

names = ["Tab 1", "Tab 2", "Tab 3"]
current_tab = to_ref(names[0])


with rxui.tab_panels(current_tab):
    for name in names:
        with rxui.tab_panel(name):
            ui.label(f"Content of {name}")

# tabs 不必在 panels 前面
with rxui.tabs(current_tab):
    for name in names:
        rxui.tab(name)

rxui.select(names, value=current_tab)
rxui.radio(names, value=current_tab).props("inline")

rxui.label(lambda: f"当前 tab 为:{current_tab.value}")

BI 模块

以最精简的 apis 创建可交互的数据可视化报表

from nicegui import ui
import pandas as pd
import numpy as np
from ex4nicegui import bi
from ex4nicegui.reactive import rxui
from ex4nicegui import effect, effect_refreshable
from pyecharts.charts import Bar


# data ready
def gen_data():
    np.random.seed(265)
    field1 = ["a1", "a2", "a3", "a4"]
    field2 = [f"name{i}" for i in range(1, 11)]
    df = (
        pd.MultiIndex.from_product([field1, field2], names=["cat", "name"])
        .to_frame()
        .reset_index(drop=True)
    )
    df[["idc1", "idc2"]] = np.random.randint(50, 1000, size=(len(df), 2))
    return df


df = gen_data()

# 创建数据源
ds = bi.data_source(df)

# ui
ui.query(".nicegui-content").classes("items-stretch no-wrap")

with ui.row().classes("justify-evenly"):
    # 基于数据源 `ds` 创建界面组件
    ds.ui_select("cat").classes("min-w-[10rem]")
    ds.ui_select("name").classes("min-w-[10rem]")


with ui.grid(columns=2):
    # 使用字典配置图表
    @ds.ui_echarts
    def bar1(data: pd.DataFrame):
        data = data.groupby("name").agg({"idc1": "sum", "idc2": "sum"}).reset_index()

        return {
            "xAxis": {"type": "value"},
            "yAxis": {
                "type": "category",
                "data": data["name"].tolist(),
                "inverse": True,
            },
            "legend": {"textStyle": {"color": "gray"}},
            "series": [
                {"type": "bar", "name": "idc1", "data": data["idc1"].tolist()},
                {"type": "bar", "name": "idc2", "data": data["idc2"].tolist()},
            ],
        }

    bar1.classes("h-[20rem]")

    # 使用pyecharts配置图表
    @ds.ui_echarts
    def bar2(data: pd.DataFrame):
        data = data.groupby("name").agg({"idc1": "sum", "idc2": "sum"}).reset_index()

        return (
            Bar()
            .add_xaxis(data["name"].tolist())
            .add_yaxis("idc1", data["idc1"].tolist())
            .add_yaxis("idc2", data["idc2"].tolist())
        )

    bar2.classes("h-[20rem]")

    # 绑定点击事件,即可实现跳转
    @bar2.on_chart_click
    def _(e: rxui.echarts.EChartsMouseEventArguments):
        ui.open(f"/details/{e.name}", new_tab=True)


# 利用响应式机制,你可以随意组合原生 nicegui 组件
label_a1_total = ui.label("")


# 当 ds 有变化,都会触发此函数
@effect
def _():
    # filtered_data 为过滤后的 DataFrame
    df = ds.filtered_data
    total = df[df["cat"] == "a1"]["idc1"].sum()
    label_a1_total.text = f"idc1 total(cat==a1):{total}"


# 你也可以使用 `effect_refreshable`,但需要注意函数中的组件每次都被重建
@effect_refreshable
def _():
    df = ds.filtered_data
    total = df[df["cat"] == "a2"]["idc1"].sum()
    ui.label(f"idc1 total(cat==a2):{total}")


# 当点击图表系列时,跳转的页面
@ui.page("/details/{name}")
def details_page(name: str):
    ui.label("This table data will not change")
    ui.aggrid.from_pandas(ds.data.query(f'name=="{name}"'))

    ui.label("This table will change when the homepage data changes. ")

    @bi.data_source
    def new_ds():
        return ds.filtered_data[["name", "idc1", "idc2"]]

    new_ds.ui_aggrid()


ui.run()

细节

bi.data_source

数据源是 BI 模块的核心概念,所有数据的联动基于此展开。当前版本(0.4.3)中,有两种创建数据源的方式

接收 pandasDataFrame:

from nicegui import ui
from ex4nicegui import bi
import pandas as pd

df = pd.DataFrame(
    {
        "name": list("aabcdf"),
        "cls": ["c1", "c2", "c1", "c1", "c3", None],
        "value": range(6),
    }
)

ds =  bi.data_source(df)

有时候,我们希望基于另一个数据源创建新的数据源,此时可以使用装饰器创建联动数据源:

df = pd.DataFrame(
    {
        "name": list("aabcdf"),
        "cls": ["c1", "c2", "c1", "c1", "c3", None],
        "value": range(6),
    }
)

ds =  bi.data_source(df)

@bi.data_source
def new_ds():
    # df is pd.DataFrame 
    df = ds.filtered_data
    df=df.copy()
    df['value'] = df['value'] * 100
    return df

ds.ui_select('name')
new_ds.ui_aggrid()

注意,由于 new_ds 中使用了 ds.filtered_data ,因此 ds 的变动会触发 new_ds 的联动变化,从而导致 new_ds 创建的表格组件产生变化


通过 ds.remove_filters 方法,移除所有筛选状态:

ds = bi.data_source(df)

def on_remove_filters():
    ds.remove_filters()

ui.button("remove all filters", on_click=on_remove_filters)

ds.ui_select("name")
ds.ui_aggrid()

通过 ds.reload 方法,重设数据源:

df = pd.DataFrame(
    {
        "name": list("aabcdf"),
        "cls": ["c1", "c2", "c1", "c1", "c3", None],
        "value": range(6),
    }
)

new_df = pd.DataFrame(
    {
        "name": list("xxyyds"),
        "cls": ["cla1", "cla2", "cla3", "cla3", "cla3", None],
        "value": range(100, 106),
    }
)

ds = bi.data_source(df)

def on_remove_filters():
    ds.reload(new_df)

ui.button("reload data", on_click=on_remove_filters)

ds.ui_select("name")
ds.ui_aggrid()

ui_select

from nicegui import ui
from ex4nicegui import bi
import pandas as pd

df = pd.DataFrame(
    {
        "name": list("aabcdf"),
        "cls": ["c1", "c2", "c1", "c1", "c3", None],
        "value": range(6),
    }
)

ds = bi.data_source(df)

ds.ui_select("name")

第一个参数 column 指定数据源的列名


通过参数 sort_options 设置选项顺序:

ds.ui_select("name", sort_options={"value": "desc", "name": "asc"})

参数 exclude_null_value 设置是否排除空值:

df = pd.DataFrame(
    {
        "cls": ["c1", "c2", "c1", "c1", "c3", None],
    }
)

ds = bi.data_source(df)
ds.ui_select("cls", exclude_null_value=True)

你可以通过关键字参数,设置原生 nicegui select 组件的参数.

通过 value 属性,设置默认值:

ds.ui_select("cls",value=['c1','c2'])
ds.ui_select("cls",multiple=False,value='c1')

多选时(参数 multiple 默认为 True),value 需要指定为 list

单选时,value 设置为非 list


ui_table

表格

from nicegui import ui
from ex4nicegui import bi
import pandas as pd

data = pd.DataFrame({"name": ["f", "a", "c", "b"], "age": [1, 2, 3, 1]})
ds = bi.data_source(data)

ds.ui_table(
    columns=[
        {"label": "new colA", "field": "colA", "sortable": True},
    ]
)
  • columns 与 nicegui ui.table 一致。其中 键值 field 对应数据源的列名,如果不存在,则该配置不会生效
  • rows 参数不会生效。因为表格的数据源始终由 data source 控制

ui_aggrid

from nicegui import ui
from ex4nicegui import bi
import pandas as pd

data = pd.DataFrame(
    {
        "colA": list("abcde"),
        "colB": [f"n{idx}" for idx in range(5)],
        "colC": list(range(5)),
    }
)
df = pd.DataFrame(data)

source = bi.data_source(df)

source.ui_aggrid(
    options={
        "columnDefs": [
            {"headerName": "xx", "field": "no exists"},
            {"headerName": "new colA", "field": "colA"},
            {
                "field": "colC",
                "cellClassRules": {
                    "bg-red-300": "x < 3",
                    "bg-green-300": "x >= 3",
                },
            },
        ],
        "rowData": [{"colX": [1, 2, 3, 4, 5]}],
    }
)
  • 参数 options 与 nicegui ui.aggrid 一致。其中 columnDefs 中的键值 field 对应数据源的列名,如果不存在,则该配置不会生效
  • rowData 键值不会生效。因为表格的数据源始终由 data source 控制