Giter Club home page Giter Club logo

ex4nicegui's Introduction

ex4nicegui

简体中文| English

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()

ViewModel

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

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

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

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

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

# 每个对象拥有独立的数据
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.result() < 0 else "black"
    )

cached_var

上面的示例中,由于使用了两次 calc.result 。因此,每当 num1, sign, num2 任意一个值发生变化时,result 都会执行2次。

实际上,第二次的计算是多余的。我们可以通过添加 rxui.cached_var 装饰器,避免多余的计算。

class Calculator(rxui.ViewModel):
    ...

    @rxui.cached_var
    def result(self):
        return eval(f"{self.num1.value}{self.sign.value}{self.num2.value}")

...

使用列表

当数据为可变对象时,比如列表,字典等,需要提供工厂函数传给 rxui.var

class Home(rxui.ViewModel):
    persons= rxui.var(lambda: [])

下面的示例,每个 person 使用卡片展示。最上方显示所有人的平均年龄。当个人年龄大于平均年龄,卡片外边框将变为红色。 通过 number 组件修改年龄,一切都会自动更新。

from ex4nicegui import rxui, Ref
from itertools import count

id_generator = count()

class Person(rxui.ViewModel):
    name = rxui.var("")
    age = rxui.var(0)

    def __init__(self, name: str = "", age: int = 0):
        super().__init__()
        self.name.value = name
        self.age.value = age
        self.id = next(id_generator)


class Home(rxui.ViewModel):
    persons: Ref[List[Person]] = rxui.var(lambda: [])

    def avg_age(self) -> float:
        if len(self.persons.value) == 0:
            return 0

        return sum(p.age.value for p in self.persons.value) / len(self.persons.value)

    def sample_data(self):
        self.persons.value = [
            Person("alice", 25),
            Person("bob", 30),
            Person("charlie", 31),
            Person("dave", 22),
            Person("eve", 26),
            Person("frank", 29),
        ]

home = Home()
home.sample_data()

rxui.label(lambda: f"平均年龄: {home.avg_age()}")


with ui.row():

    @rxui.vfor(home.persons, key="id")
    def _(store: rxui.VforStore[Person]):
        person = store.get_item()
        with rxui.card().classes("outline").bind_classes(
            {
                "outline-red-500": lambda: person.age.value > home.avg_age(),
            }
        ):
            rxui.input(value=person.name, placeholder="名字")
            rxui.number(value=person.age, min=1, max=100, step=1, placeholder="年龄")

更多复杂的应用,可以查看 examples


响应式

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])
rxui.label("binding to single string").bind_classes(bg_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}")

lazy_tab_panels

懒加载模式下,只有当前激活的 tab 才会渲染。

from ex4nicegui import to_ref, rxui, on, deep_ref

current_tab = to_ref("t1")

with rxui.tabs(current_tab):
    ui.tab("t1")
    ui.tab("t2")

with rxui.lazy_tab_panels(current_tab) as panels:

    @panels.add_tab_panel("t1")
    def _():
        ui.notify("Hello from t1")

    @panels.add_tab_panel("t2")
    def _():
        ui.notify("Hello from t2")

页面加载后,立刻显示 "Hello from t1"。当切换到 "t2" 页签,才会显示 "Hello from t2"。


scoped_style

scoped_style 方法允许你创建限定在组件内部的样式。

# 所有子元素都会有红色轮廓,但排除自身
with rxui.row().scoped_style("*", "outline: 1px solid red;") as row:
    ui.label("Hello")
    ui.label("World")


# 所有子元素都会有红色轮廓,包括自身
with rxui.row().scoped_style(":self *", "outline: 1px solid red;") as row:
    ui.label("Hello")
    ui.label("World")

# 当鼠标悬停在 row 组件时,所有子元素都会有红色轮廓,但排除自身
with rxui.row().scoped_style(":hover *", "outline: 1px solid red;") as row:
    ui.label("Hello")
    ui.label("World")

# 当鼠标悬停在 row 组件时,所有子元素都会有红色轮廓,包括自身
with rxui.row().scoped_style(":self:hover *", "outline: 1px solid red;") as row:
    ui.label("Hello")
    ui.label("World")

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 控制

ex4nicegui's People

Contributors

crystalwindsnake avatar githubccww avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

ex4nicegui's Issues

Improve rxui.use_draggable

Currently, rxui.use_draggable obtains the responsive variable of the drag and drop position.

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

with ui.card().classes("select-none") as card:
    ui.label("卡片")

dg = rxui.use_draggable(card)

@ref_computed
def position_text():
    return f"x:{dg.x.value},y:{dg.y.value}"

rxui.label(position_text)

However, if it is supported to pass in a responsive variable from outside, this may be more useful.

x = to_ref(100.0)
y = to_ref(100.0)


@ref_computed
def position_text():
    return f"x:{x.value},y:{y.value}"


# ui
with ui.card().classes("select-none") as card:
    ui.label("卡片")
    rxui.label(position_text)

dg = rxui.use_draggable(card, init_x=x, init_y=y)

this example allows data to be separated from interface code.

[Bug] TypeError: Type is not JSON serializable: TabsBindableUi

rxui.tabs is not working properly:

from ex4nicegui import rxui
from nicegui import ui

with rxui.tabs() as tabs:
    tab = rxui.tab("Tab 1")
with rxui.tab_panels(tabs):
    with rxui.tab_panel(tab):
        rxui.label("1")

ui.run()

Will raise TypeError: Type is not JSON serializable: TabsBindableUi
Might be related to #175

rxui.table selection_ref had no data at the first times

from nicegui import ui
import pandas as pd

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


df = pd.DataFrame({"name": list("abcd"), "value": [1, 2, 3, 4]})
r_current_data = to_ref(df)


def onselect():
    print(table.selection_ref.value)


table = rxui.table.from_pandas(
    r_current_data, row_key="name", selection="multiple", on_select=onselect
)

When any row is checked, print result is an empty list.

Error when vfor binds a list in a dictionary

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


rxui.label(data)


@rxui.vfor(data.value["a"])
def _(s):
    rxui.label(s.get())
    rxui.input(value=rxui.vmodel(s.get()))

err

AttributeError: 'ListProxy' object has no attribute 'value'

rxui.echarts update options incorrectly

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


opts = {
    "title": {"text": "title", "bottom": 0},
    "xAxis": {"type": "value"},
    "yAxis": {
        "type": "category",
        "data": ["a", "b"],
    },
    "series": [
        {
            "name": "first",
            "type": "bar",
            "data": [18203, 23489],
        },
        {
            "name": "second",
            "type": "bar",
            "data": [19325, 23438],
        },
    ],
}

r_opts = to_ref(opts)

bar = rxui.echarts(r_opts)


def on_click():
    del r_opts.value["title"]["bottom"]
    r_opts.value = r_opts.value


ui.button("del title bottom", on_click=on_click)

When the button is clicked, the title position should return to the top left corner, but it doesn't

rxui.echarts from_javascript method fails to create maps

The following code does not display the map:

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

rxui.echarts.from_javascript(
    r"""
chart =>{
                             
    chart.setOption({
        "geo": {
            "map": "china",
            "roam": True,
        },
        "tooltip": {},
        "legend": {},
        "series": [],
    });

}
"""
)

Add-unique mode of rxui.select not trigger changes in options

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

r_str = to_ref(None)
r_opts = to_ref([])

@effect
def _():
    # Adding values in the select component does not trigger execution either.
    print(r_opts.value)

@ui.page("/")
def _():
    rxui.select(
        r_opts, clearable=True, value=r_str, new_value_mode="add-unique"
    ).classes("min-w-[20ch]")

    rxui.label(r_opts)

After typing 'a' in the select and confirming with Enter, the label below should display ['a'], but currently, there is no change.

rxui.echarts click event error

from ex4nicegui.reactive import rxui


echart = rxui.echarts(
    {
        "xAxis": {"type": "value"},
        "yAxis": {
            "type": "category",
            "data": ["A", "B"],
            "inverse": True,
        },
        "legend": {
            "textStyle": {"color": "gray"}
        },
        "series": [
            {
                "type": "bar",
                "name": "Alpha",
                "data": [0.1, 0.2],
            },
            {
                "type": "bar",
                "name": "Beta",
                "data": [0.3, 0.4],
            },
        ],
    }
)

def onclick(e):
    print(e)

echart.on(
    "click",
    onclick
)

error:

TypeError: EChartsMouseEventArguments. init() takes 1 positional argument but 3 positional alord-only arguments ) were given

improve event batch api

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

a = to_ref(0)
b = to_ref(0)
text = ref_computed(lambda: f"a={a.value};b={b.value}")

@on([a, b, text])
def when_vars_changed():
    ui.notify(f"a:{a.value};b:{b.value};text={text.value}")

def when_click():
    a.value += 1
    b.value += 1

ui.button("change all values", on_click=when_click)
ui.run()

Every time the button is clicked, notify will be triggered twice. because modifying a reactive variable always immediately triggers a reactive event.

we can improve it in this way

...

def when_click():
    @batch
    def _():
        a.value += 1
        b.value += 1

now,button clicked,notify will be triggered only once.

Perhaps we can provide an event_batch decorator that is used like this:

@event_batch
def when_click():
    a.value += 1
    b.value += 1

Loss of responsiveness, when using shared pages

from ex4nicegui import rxui, to_ref

a = to_ref("x")

rxui.input(value=a)
rxui.label(text=a)

When the browser opens 2 pages and then deletes one of them, the other page loses responsiveness.

If you use the page defined by ui.page, there is no problem

AssertionError: must be pyecharts chart object

from nicegui import ui
from pyecharts.charts import Bar
from ex4nicegui import ref_computed, to_ref
from ex4nicegui.reactive import rxui

show = to_ref(True)


@ref_computed
def chart():
    if not show.value:
        return None

    c = (
        Bar()
        .add_xaxis(
            [
                "Mon",
                "Tue",
                "Wed",
                "Thu",
                "Fri",
                "Sat",
                "Sun",
            ]
        )
        .add_yaxis(
            "商家A",
            [120, 200, 150, 80, 70, 110, 130],
        )
    )

    return c


rxui.checkbox("show", value=show)
rxui.echarts.from_pyecharts(chart)

raise error when click the checkbox

response value of the select box is not correct.

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

data = {
    "opts1": [f"{n}{i}" for i, n in enumerate("abcd")],
    "opts2": [f"{n}{i}" for i, n in enumerate("mnxy")],
}

value1 = to_ref("opts1")
value2 = to_ref("")


@ref_computed
def opts2():
    return data[value1.value]


@on(opts2)
def _():
    value2.value = opts2.value[0]


rxui.select(list(data.keys()), value=value1)
rxui.select(opts2, value=value2)

rxui.label(value1)
rxui.label(value2)

When the first select box changed from option opts1 to opts2, the value of the second select box should have been m0, but now the value is None

rxui table no works use deep ref rows

columns = [
    {
        "name": "a",
        "label": "a",
        "field": "a",
    },
    {"name": "b", "label": "b", "field": "b"},
]

rows = deep_ref(
    [
        {"a": "n1", "b": 18},
        {"a": "n2", "b": 21},
    ]
)

rxui.table(columns=columns, rows=rows, row_key="name")

def onclick():
    rows.value.append(
        {"a": "n3"},
    )

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

table should add new row when click button,but not now

bind classes should need to support direct function passing

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)
# it works
rxui.label("bind to ref_computed").bind_classes(class_obj)

# not works
rxui.label("bind to ref_computed").bind_classes([class_obj](lambda: {"bg-blue": bg_color.value, "text-red": has_error.value}))

rxui.echarts.from_pyecharts not support js code

from nicegui import ui
from ex4nicegui.reactive import rxui

from pyecharts import options as opts
from pyecharts.charts import Bar
from pyecharts.commons import utils

c = (
    Bar()
    .add_xaxis(["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"])
    .add_yaxis(
        "商家A",
        [120, 200, 150, 80, 70, 110, 130],
    )
    .set_global_opts(
        yaxis_opts=opts.AxisOpts(
            axislabel_opts={
                "formatter": utils.JsCode(
                    """function (value, index) {
            return value + 'kg';
        }"""
                )
            }
        )
    )
)


rxui.echarts.from_pyecharts(c)

error:

simplejson.errors.JSONDecodeError: Expecting value: line 148 column 30

rxui.use_pagination parameter source using interface type

This parameter only needs to support the len and slicing interfaces.

r_df = to_ref(pd.DataFrame({"value": range(100)}))
pagination = rxui.use_pagination(source = r_df)

Currently, the type annotation of source parameter is list, but an interface type should be used.

rxui.vmodel executes slowly

from nicegui import ui
from ex4nicegui import deep_ref, rxui
from ex4nicegui.utils.signals import to_ref_wrapper
import time


def calculate_execution_time(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(
            f"Function '{func.__name__}' took {end_time - start_time} seconds to execute."
        )
        return result

    return wrapper


@calculate_execution_time
def use_vmodel():
    data = deep_ref({"a": "text"})

    rxui.label(data)
    rxui.input(value=rxui.vmodel(data.value["a"]))


use_vmodel()


@calculate_execution_time
def use_to_wrap():
    data = deep_ref({"a": "text"})

    def setter(new_value):
        data.value["a"] = new_value

    wrapper = to_ref_wrapper(lambda: data.value["a"], setter)

    rxui.label(data)
    rxui.input(value=wrapper)


use_to_wrap()

ui.run()

Using rxui.vmodel is much slower than using to_ref_wrapper

Function 'use_vmodel' took 0.6890246868133545 seconds to execute.
Function 'use_to_wrap' took 0.0 seconds to execute.

AttributeError: 'echarts' object has no attribute 'on_chart_click'

“@bar2.on_chart_click”报错。

# 使用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 cl(e):
    ui.open(f"/details/{e.name}", new_tab=True)

lazy_input no responsive value triggered on clicking the clear icon

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


r_value = to_ref("")

rxui.lazy_input(value=r_value).props("outlined clearable")

rxui.label(r_value)
  1. Enter the content "text" and the content is displayed below
  2. Click the clear icon in the input box, the content below should disappear at the same time, but now it doesn't

echarts cannot use deep ref as options

from nicegui import ui
from ex4nicegui import deep_ref, rxui

r_opts = deep_ref(
    {
        "xAxis": {
            "type": "category",
            "data": ["Mon", "Tue", "Wed"],
        },
        "yAxis": {"type": "value"},
        "series": [{"data": [120, 200, 150], "type": "bar"}],
    }
)


rxui.number(value=rxui.vmodel(r_opts.value["series"][0]["data"][0]))
rxui.echarts(r_opts, not_merge=False)

the above code reports an error

ds.ui_select appears duplicate selected values

from ex4nicegui import bi
import pandas as pd

df = pd.DataFrame(
    {
        "cls1": [
            "x1",
            "x1",
            "x1",
            "x2",
            "x2",
            "x2",
        ],
        "cls2": ["a", "b", "c", "c", "b", "a"],
    }
)
ds = bi.data_source(df)


ds.ui_select("cls1", value="x1", multiple=False)
ds.ui_select("cls2")
  1. select 'a' in the cls2 dropdown box
  2. select 'x2' in the cls1 dropdown box
  3. select 'a' in the cls2 dropdown box

At this point, the cls2 dropdown box has two selected 'a' values, which is correct. There should be no selected values.

rxui.radio not support dict options

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

options = to_ref({'a': '我是a', 'b': '我是b'})
rxui.radio(options=options)

ui.run()

console error when click radio option:

KeyError: 0

rxui.number type hints error

value = deep_ref(1)
rxui.number("number", min=0, max=100, step=1, value=value)

type hints error

Unable to assign parameter of type "Ref[int]" to parameter "value" of type "_TMaybeRef[float] | None" in function "__init__".

rxui.select error when multiple

rxui.select(["a", "b", "c"], multiple=True)

when select item,error show:

TypeError: list indices must be integers or slices, not str

bind_style type hint error

text = to_ref("")
opacity = to_ref(1)


rxui.lazy_slider(min=0, max=1, step=0.1, value=opacity)
rx_label(text).bind_style({"opacity": opacity})
rxui.input(value=text)

No problem with the program, just a warning on the type hint

rxui.select can not support new value mode

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

r_value = to_ref(None)

@effect
def _():
    print(r_value.value)

rxui.select([], value=r_value, new_value_mode="add-unique")

it doesn't work

rxui.button not support "with" statement

I hope support like this

from ex4nicegui.reactive import rxui

with rxui.button(""):
    ui.avatar("home")

but it not ,just like this

from ex4nicegui.reactive import rxui

with rxui.button("").element:
    ui.avatar("home")

slider value not effect

value = to_ref(0)
rxui.slider(min=0, max=1, step=0.1, value=value)
rxui.label(value)

label test not change

Creating maps with echarts

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": [],
    }
)

[Bug] Number value should be None instead of 0 when number input is emptied

Thanks for the quick fix. Here is another issue I found:

from ex4nicegui import rxui, to_ref
from nicegui import ui

num = to_ref(None)
rxui.number(value=num)
ui.button('Increment', on_click=lambda: ui.notify(num.value))
ui.run()

When running the above code, filling some numbers and deleting them, num becomes 0 (instead of None) which is not expected.

negative values for the current page number in the use_pagination

from nicegui import ui
from ex4nicegui import on
from ex4nicegui.reactive import use_pagination


page = use_pagination(list(range(100)))


@on(page.current_page)
def _():
    print(page.current_page.value)


ui.button("prev page", on_click=page.prev)
ui.button("next page", on_click=page.next)


ui.run()

If you keep clicking the previous page, the value of the current page in the console will be less than 0.

aggrid cannot set group header

aggrid in nicegui allows grouped table headers, but not in the bi module in ex4nicegui.

grid = ui.aggrid(
    {
        "defaultColDef": {"flex": 1},
        "columnDefs": [
            {
                "headerName": "xxxx",
                "children": [
                    {
                        "field": "name",
                    },
                    {
                        "field": "age",
                    },
                ],
            },
            {
                "field": "parent",
            },
        ],
        "rowData": [
            {"name": "Alice", "age": 18, "parent": "David"},
            {"name": "Bob", "age": 21, "parent": "Eve"},
            {"name": "Carol", "age": 42, "parent": "Frank"},
        ],
        "rowSelection": "multiple",
    }
).classes("max-h-40")


ds = bi.data_source(
    pd.DataFrame(
        {"name": "Alice", "age": 18, "parent": "David"},
        {"name": "Bob", "age": 21, "parent": "Eve"},
        {"name": "Carol", "age": 42, "parent": "Frank"},
    )
)

ds.ui_aggrid(
    options={
        "columnDefs": [
            {
                "headerName": "xxxx",
                "children": [
                    {
                        "field": "name",
                    },
                    {
                        "field": "age",
                    },
                ],
            },
            {
                "field": "parent",
            },
        ],
    }
)

rxui.echarts TypeError: Type is not JSON serializable: ListProxy

from nicegui import ui
from ex4nicegui import deep_ref, rxui, to_raw


series_data = deep_ref([120, 200, 150])


def opts():
    return {
        "xAxis": {
            "type": "category",
            "data": ["Mon", "Tue", "Wed"],
        },
        "yAxis": {"type": "value"},
        "series": [{"data": series_data.value, "type": "bar"}],
    }


rxui.number(value=rxui.vmodel(series_data.value[0]))
rxui.echarts(opts, not_merge=False)

error:

TypeError: Type is not JSON serializable: ListProxy

If to raw is used, responsive is lost:

...
 "series": [{"data": to_raw(series_data.value), "type": "bar"}],
...

You have to convert to list and then use to raw to get it to work.

...
 "series": [{"data": to_raw(list(series_data.value)), "type": "bar"}],
...

missing 1 required positional argument: 'e'

from nicegui import ui
from ex4nicegui.reactive import rxui

def onchange(e):
    print(e.value)

rxui.input(on_change=onchange)

ui.run()

error when type input

TypeError: onchange() missing 1 required positional argument: 'e'

Button text bind not work

bug:

from ex4nicegui.reactive import rxui

text = to_ref("old text")
rxui.button(text).props("no-caps")
text.value='new text'

button text still 'old text'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.