ex4nicegui


Nameex4nicegui JSON
Version 0.6.6 PyPI version JSON
download
home_pageNone
SummaryExtension library based on nicegui, providing data responsive,BI functionality modules
upload_time2024-05-15 16:43:08
maintainerNone
docs_urlNone
authorcarson_jia
requires_python>=3.8
licenseMIT license
keywords nicegui ex4nicegui webui
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # ex4nicegui
[δΈ­ζ–‡ README](./README.md)

- [Install](#-install)
- [Usage](#-usage)
- [Features](#-features)
- [BI Module](#bi-module)

An extension library for [nicegui](https://github.com/zauberzeug/nicegui). It has built-in responsive components and fully implements data-responsive interface programming.


## πŸ“¦ Install

```
pip install ex4nicegui -U
```

## examples
- [basic](./examples/basic/)
- [todo list mvc](./examples/todomvc/)


## πŸ¦„ Usage

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

# Define responsive data
r_input = to_ref("")

# Pass in the responsive data according to the nicegui usage method.
rxui.input(value=r_input)
rxui.label(r_input)

ui.run()
```
![](./asset/sync_input.gif)


## πŸš€ Features

### echarts components

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

r_input = to_ref("")


# ref_computed Creates a read-only reactive variable
# Functions can use any other reactive variables automatically when they are associated
@ref_computed
def cp_echarts_opts():
    return {
        "title": {"text": r_input.value}, #In the dictionary, use any reactive variable. Get the value by .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("Enter the content, and the chart title will be synchronized", value=r_input)

# Get the native nicegui component object through the element attribute of the responsive component object
input.element.classes("w-full")

rxui.echarts(cp_echarts_opts).classes('h-[20rem]')

ui.run()
```
![](./asset/asyc_echarts_title.gif)


### echarts mouse events

the `on` function parameters `event_name` and `query` to view the[echarts English Documentation](https://echarts.apache.org/handbook/en/concepts/event/)


The following example binds the mouse click event
```python
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)
```


The following example only triggers the mouseover event for a specified series.
```python
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()
```
---


## responsive

```python
from ex4nicegui import (
    to_ref,
    ref_computed,
    on,
    effect,
    effect_refreshable,
    batch,
    event_batch,
    deep_ref,
)
```
Commonly used `to_ref`,`deep_ref`,`effect`,`ref_computed`,`on`

### `to_ref`
Defines responsive objects, read and written by `.value`.
```python
a = to_ref(1)
b = to_ref("text")

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

print(a.value)
```

When the value is a complex object, responsiveness of nested objects is not maintained by default.

```python
a = to_ref([1,2])

@effect
def _().
    print(len(a.value))

# doesn't trigger the effect
a.value.append(10)

# the whole substitution will be triggered
a.value = [1,2,10]
```

Depth responsiveness is obtained when `is_deep` is set to `True`.

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

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

# print 3
a.value.append(10)

```

> `deep_ref` is equivalent to `to_ref` if `is_deep` is set to `True`.
---

### `deep_ref`
Equivalent to `to_ref` when `is_deep` is set to `True`.

Especially useful when the data source is a list, dictionary or custom class. Objects obtained via `.value` are proxies

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

assert data_ref.value is not data
```

You can get the raw object with `to_raw`.
```python
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`
Accepts a function and automatically monitors changes to the responsive objects used in the function to automatically execute the function.

```python
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)
```

The first time the effect is executed, the function `auto_run_when_ref_value` will be executed once. After that, clicking on the button changes the value of `a` (via `a.value`) and the function `auto_run_when_ref_value` is executed again.

> Never spread a lot of data processing logic across multiple `on`s or `effects`s, which should be mostly interface manipulation logic rather than responsive data processing logic

---

### `ref_computed`
As with `effect`, `ref_computed` can also return results from functions. Typically used for secondary computation from `to_ref`.

```python
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)
```

When the button is clicked, the value of `a.value` is modified, triggering a recalculation of `a_square`. As the value of `a_square` is read in `effect1`, it triggers `effect1` to execute the

> `ref_computed` is read-only `to_ref`

If you prefer to organize your code by class, ``ref_computed`` also supports acting on instance methods

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

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

state = MyState()

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

---

### `async_computed`
Use `async_computed` when asynchronous functions are required for computed.

```python

# Simulate asynchronous functions that take a long time to execute
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: "Query in progress" if evaluating.value else "Enter content in input box above and return to search"
)
rxui.label(search_result)

```

- The first argument to `async_computed` must explicitly specify the responsive data to be monitored. Multiple responses can be specified using a list.
- Parameter `evaluating` is responsive data of type bool, which is `True` while the asynchronous function is executing, and `False` after the computation is finished.
- Parameter `init` specifies the initial result

---

### `on`
Similar to `effect`, but `on` needs to explicitly specify the responsive object to monitor.

```python

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)

```

- If the parameter `onchanges` is True (the default value is False), the specified function will not be executed at binding time.

> Never spread a lot of data processing logic across multiple `on`s or `effects`s, which should be mostly interface manipulation logic rather than responsive data processing logic

---

### `new_scope`

By default, all watch functions are automatically destroyed when the client connection is disconnected. For finer-grained control, the `new_scope` function can be utilized.

```python
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)
```

---

## functionality

### vmodel
Create two-way bindings on form input elements or components.

Bidirectional bindings are supported by default for `ref` simple value types
```python
from ex4nicegui import rxui, to_ref, deep_ref

data = to_ref("init")

rxui.label(lambda: f"{data.value=}")
# two-way binding 
rxui.input(value=data)
```
- Simple value types are generally immutable values such as `str`, `int`, etc.

When using complex data structures, `deep_ref` is used to keep nested values responsive.
```python
data = deep_ref({"a": 1, "b": [1, 2, 3, 4]})

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

# No binding effect
rxui.input(value=data.value["a"])

# readonly binding
rxui.input(value=lambda: data.value["a"])

# two-way binding
rxui.input(value=rxui.vmodel(data.value["a"]))
```

- The first input box will be completely unresponsive because the code is equivalent to passing in a value `1` directly
- The second input box will get read responsiveness due to the use of a function.
- The third input box, wrapped in `rxui.vmodel`, can be bi-directionally bound.

Most use `vmodel` with `vfor`, see [todo list examples](./examples/todomvc/)

---

### vfor
Render list components based on list responsive data. Each component is updated on demand. Data items support dictionaries or objects of any type

```python
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]):
    # function to build the interface for each row of data
    item = store.get()  # Get responsive object with `store.get`
    mes = rxui.vmodel(item.value['message'])

    # Enter the content of the input box, 
    # you can see the title of the radio box changes synchronously
    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` decorator to custom function
    - The first argument is passed to the responsive list. Each item in the list can be a dictionary or other object (`dataclasses` etc.)
    - Second parameter `key`: In order to be able to keep track of the identity of each node, and thus reuse and reorder existing elements, you can provide a unique key for the block corresponding to each element. The default(`None`) is to use the list element index.
- The custom function takes one argument. The current row's  can be retrieved via `store.get`, which is a responsive object.


> vfor are created only when new data is added.


---


### Bind class names

All component classes provide `bind_classes` for binding `class`, supporting three different data structures.

Bind dictionaries

```python
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)
```

Dictionary key  is the class name, and a dictionary value of  a responsive variable with value `bool`. When the responsive value is `True`, the class name is applied to the component `class`.

---

Bind a responsive variable whose return value is a dictionary.

```python
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}
)
```

---

Bind to list

```python
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])

```

Each element in the list is a responsive variable that returns the class name

---

### bind-style

```python
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` passed into dictionary, `key` is style name, `value` is style value, responsive string

---

### rxui.echarts
Charting with echarts

---

#### rxui.echarts.from_javascript
Create echart from javascript code

```python
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);
}
"""
)
```

- The first parameter of the function is the echart instance object. You need to configure the chart in the function with `setOption`.

---

#### rxui.echarts.register_map
Register a map.

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

- The parameter `map_name` is a customized map name. Note that `map` must correspond to a registered name in the chart configuration.
- The parameter `src` is a valid network link to the map data.

You can also provide the path to the local json file for the map data.
```python
from pathlib import Path

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

---

### gsap

js animation library. [gsap documentation](https://gsap.com/docs/v3/)

```python
from nicegui import ui
from ex4nicegui import gsap
```

#### gsap.from_

Set the start property, the animation will transition from the set property to the original position

```python

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

```

After the screen is loaded, the starting position of the text is shifted to the right by 50px, and then moved to the original position within 1 second.

- Arguments `targets` are css selectors.
- Arguments `vars` are attribute values

---

#### gsap.to

Set the end property, the animation will transition from the original property to the set property.

```python

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

```

After loading the screen, the text will be moved back 50px from the original position within 1 second.

- Arguments `targets` are css selectors.
- Arguments `vars` are attribute values

---

#### gsap.run_script

Setting up animations by writing js

```python

gsap.run_script(
            r"""function setGsap(gsap) {
    gsap.to('.target',{"duration": 0.3,y:60})
}
""")
```

- The parameter `script` can be text or a file with a js extension `Path`.
- The name of the defined js function doesn't matter, the first argument is a gsap object.

---

## BI Module

Create an interactive data visualization report using the minimal API.

![](./asset/bi_examples1.gif)

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

# Create a data source.
ds = bi.data_source(df)

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

with ui.row().classes("justify-evenly"):
    # Create components based on the data source `ds`.
    ds.ui_select("cat").classes("min-w-[10rem]")
    ds.ui_select("name").classes("min-w-[10rem]")


with ui.grid(columns=2):
    # Configure the chart using a dictionary.
    @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]")

    # Configure the chart using 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]")

    # Bind the click event to achieve navigation.
    @bar2.on_chart_click
    def _(e: rxui.echarts.EChartsMouseEventArguments):
        ui.open(f"/details/{e.name}", new_tab=True)


# with response mechanisms, you can freely combine native nicegui components.
label_a1_total = ui.label("")


# this function will be triggered when ds changed.
@effect
def _():
    # prop `filtered_data` is the filtered DataFrame.
    df = ds.filtered_data
    total = df[df["cat"] == "a1"]["idc1"].sum()
    label_a1_total.text = f"idc1 total(cat==a1):{total}"


# you can also use `effect_refreshable`, but you need to note that the components in the function are rebuilt each time.
@effect_refreshable
def _():
    df = ds.filtered_data
    total = df[df["cat"] == "a2"]["idc1"].sum()
    ui.label(f"idc1 total(cat==a2):{total}")


# the page to be navigated when clicking on the chart series.
@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()
```

### Details

#### `bi.data_source`
The data source is the core concept of the BI module, and all data linkage is based on this. In the current version (0.4.3), there are two ways to create a data source.

Receive `pandas`'s `DataFrame`:
```python
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)
```

Sometimes, we want to create a new data source based on another data source, in which case we can use a decorator to create a linked data source:
```python
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()
```

Note that since `new_ds` uses `ds.filtered_data`, changes to `ds` will trigger the linkage change of `new_ds`, causing the table component created by `new_ds` to change.

---

Remove all filter states through the `ds.remove_filters` method:
```python
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()
```
---

Reset the data source through the `ds.reload` method:
```python

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

Dropdown Select Box

```python
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")
```

The first parameter column specifies the column name of the data source.

---
Set the order of options using the parameter `sort_options`:
```python
ds.ui_select("name", sort_options={"value": "desc", "name": "asc"})
```

---
Set whether to exclude null values using the parameter `exclude_null_value`:
```python
df = pd.DataFrame(
    {
        "cls": ["c1", "c2", "c1", "c1", "c3", None],
    }
)

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

---
You can set the parameters of the native nicegui select component through keyword arguments.

Set default values through the value attribute:
```python
ds.ui_select("cls",value=['c1','c2'])
ds.ui_select("cls",multiple=False,value='c1')
```
For multiple selections (the parameter `multiple` is defaulted to True), `value` needs to be specified as a list. For single selections, `value` should be set to non-list.

---


#### ui_table

Table

```python
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},
    ]
)

```

- The parameter `columns` are consistent with nicegui `ui.table`. The key value `field` corresponds to the column name of the data source, and if it does not exist, this configuration will not take effect
- The `rows` parameter will not take effect. Because the data source of the table is always controlled by the data source.

---

#### ui_aggrid


```python
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]}],
    }
)
```

- The parameter `options` is consistent with nicegui `ui.aggrid`. The key value `field` in columnDefs corresponds to the column name of the data source, and if it does not exist, this configuration will not take effect.
- The `rowData` key value will not take effect. Because the data source of the table is always controlled by the data source.



            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "ex4nicegui",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "nicegui, ex4nicegui, webui",
    "author": "carson_jia",
    "author_email": "568166495@qq.com",
    "download_url": "https://files.pythonhosted.org/packages/22/35/cf3aa27af3105d619e449cb9ecd642e66f51e4ab65ef21553c3c3d5a6178/ex4nicegui-0.6.6.tar.gz",
    "platform": null,
    "description": "# ex4nicegui\r\n[\u4e2d\u6587 README](./README.md)\r\n\r\n- [Install](#-install)\r\n- [Usage](#-usage)\r\n- [Features](#-features)\r\n- [BI Module](#bi-module)\r\n\r\nAn extension library for [nicegui](https://github.com/zauberzeug/nicegui). It has built-in responsive components and fully implements data-responsive interface programming.\r\n\r\n\r\n## \ud83d\udce6 Install\r\n\r\n```\r\npip install ex4nicegui -U\r\n```\r\n\r\n## examples\r\n- [basic](./examples/basic/)\r\n- [todo list mvc](./examples/todomvc/)\r\n\r\n\r\n## \ud83e\udd84 Usage\r\n\r\n```python\r\nfrom nicegui import ui\r\nfrom ex4nicegui import ref_computed, effect, to_ref\r\nfrom ex4nicegui.reactive import rxui\r\n\r\n# Define responsive data\r\nr_input = to_ref(\"\")\r\n\r\n# Pass in the responsive data according to the nicegui usage method.\r\nrxui.input(value=r_input)\r\nrxui.label(r_input)\r\n\r\nui.run()\r\n```\r\n![](./asset/sync_input.gif)\r\n\r\n\r\n## \ud83d\ude80 Features\r\n\r\n### echarts components\r\n\r\n```python\r\nfrom nicegui import ui\r\nfrom ex4nicegui import ref_computed, effect, to_ref\r\nfrom ex4nicegui.reactive import rxui\r\n\r\nr_input = to_ref(\"\")\r\n\r\n\r\n# ref_computed Creates a read-only reactive variable\r\n# Functions can use any other reactive variables automatically when they are associated\r\n@ref_computed\r\ndef cp_echarts_opts():\r\n    return {\r\n        \"title\": {\"text\": r_input.value}, #In the dictionary, use any reactive variable. Get the value by .value\r\n        \"xAxis\": {\r\n            \"type\": \"category\",\r\n            \"data\": [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"],\r\n        },\r\n        \"yAxis\": {\"type\": \"value\"},\r\n        \"series\": [\r\n            {\r\n                \"data\": [120, 200, 150, 80, 70, 110, 130],\r\n                \"type\": \"bar\",\r\n                \"showBackground\": True,\r\n                \"backgroundStyle\": {\"color\": \"rgba(180, 180, 180, 0.2)\"},\r\n            }\r\n        ],\r\n    }\r\n\r\n\r\ninput = rxui.input(\"Enter the content, and the chart title will be synchronized\", value=r_input)\r\n\r\n# Get the native nicegui component object through the element attribute of the responsive component object\r\ninput.element.classes(\"w-full\")\r\n\r\nrxui.echarts(cp_echarts_opts).classes('h-[20rem]')\r\n\r\nui.run()\r\n```\r\n![](./asset/asyc_echarts_title.gif)\r\n\r\n\r\n### echarts mouse events\r\n\r\nthe `on` function parameters `event_name` and `query` to view the[echarts English Documentation](https://echarts.apache.org/handbook/en/concepts/event/)\r\n\r\n\r\nThe following example binds the mouse click event\r\n```python\r\nfrom nicegui import ui\r\nfrom ex4nicegui.reactive import rxui\r\n\r\nopts = {\r\n    \"xAxis\": {\"type\": \"value\", \"boundaryGap\": [0, 0.01]},\r\n    \"yAxis\": {\r\n        \"type\": \"category\",\r\n        \"data\": [\"Brazil\", \"Indonesia\", \"USA\", \"India\", \"China\", \"World\"],\r\n    },\r\n    \"series\": [\r\n        {\r\n            \"name\": \"first\",\r\n            \"type\": \"bar\",\r\n            \"data\": [18203, 23489, 29034, 104970, 131744, 630230],\r\n        },\r\n        {\r\n            \"name\": \"second\",\r\n            \"type\": \"bar\",\r\n            \"data\": [19325, 23438, 31000, 121594, 134141, 681807],\r\n        },\r\n    ],\r\n}\r\n\r\nbar = rxui.echarts(opts)\r\n\r\ndef on_click(e: rxui.echarts.EChartsMouseEventArguments):\r\n    ui.notify(f\"on_click:{e.seriesName}:{e.name}:{e.value}\")\r\n\r\nbar.on(\"click\", on_click)\r\n```\r\n\r\n\r\nThe following example only triggers the mouseover event for a specified series.\r\n```python\r\nfrom nicegui import ui\r\nfrom ex4nicegui.reactive import rxui\r\n\r\nopts = {\r\n    \"xAxis\": {\"type\": \"value\", \"boundaryGap\": [0, 0.01]},\r\n    \"yAxis\": {\r\n        \"type\": \"category\",\r\n        \"data\": [\"Brazil\", \"Indonesia\", \"USA\", \"India\", \"China\", \"World\"],\r\n    },\r\n    \"series\": [\r\n        {\r\n            \"name\": \"first\",\r\n            \"type\": \"bar\",\r\n            \"data\": [18203, 23489, 29034, 104970, 131744, 630230],\r\n        },\r\n        {\r\n            \"name\": \"second\",\r\n            \"type\": \"bar\",\r\n            \"data\": [19325, 23438, 31000, 121594, 134141, 681807],\r\n        },\r\n    ],\r\n}\r\n\r\nbar = rxui.echarts(opts)\r\n\r\ndef on_first_series_mouseover(e: rxui.echarts.EChartsMouseEventArguments):\r\n    ui.notify(f\"on_first_series_mouseover:{e.seriesName}:{e.name}:{e.value}\")\r\n\r\n\r\nbar.on(\"mouseover\", on_first_series_mouseover, query={\"seriesName\": \"first\"})\r\n\r\nui.run()\r\n```\r\n---\r\n\r\n\r\n## responsive\r\n\r\n```python\r\nfrom ex4nicegui import (\r\n    to_ref,\r\n    ref_computed,\r\n    on,\r\n    effect,\r\n    effect_refreshable,\r\n    batch,\r\n    event_batch,\r\n    deep_ref,\r\n)\r\n```\r\nCommonly used `to_ref`,`deep_ref`,`effect`,`ref_computed`,`on`\r\n\r\n### `to_ref`\r\nDefines responsive objects, read and written by `.value`.\r\n```python\r\na = to_ref(1)\r\nb = to_ref(\"text\")\r\n\r\na.value =2\r\nb.value = 'new text'\r\n\r\nprint(a.value)\r\n```\r\n\r\nWhen the value is a complex object, responsiveness of nested objects is not maintained by default.\r\n\r\n```python\r\na = to_ref([1,2])\r\n\r\n@effect\r\ndef _().\r\n    print(len(a.value))\r\n\r\n# doesn't trigger the effect\r\na.value.append(10)\r\n\r\n# the whole substitution will be triggered\r\na.value = [1,2,10]\r\n```\r\n\r\nDepth responsiveness is obtained when `is_deep` is set to `True`.\r\n\r\n```python\r\na = to_ref([1,2],is_deep=True)\r\n\r\n@effect\r\ndef _():\r\n    print('len:',len(a.value))\r\n\r\n# print 3\r\na.value.append(10)\r\n\r\n```\r\n\r\n> `deep_ref` is equivalent to `to_ref` if `is_deep` is set to `True`.\r\n---\r\n\r\n### `deep_ref`\r\nEquivalent to `to_ref` when `is_deep` is set to `True`.\r\n\r\nEspecially useful when the data source is a list, dictionary or custom class. Objects obtained via `.value` are proxies\r\n\r\n```python\r\ndata = [1,2,3]\r\ndata_ref = deep_ref(data)\r\n\r\nassert data_ref.value is not data\r\n```\r\n\r\nYou can get the raw object with `to_raw`.\r\n```python\r\nfrom ex4nicegui import to_raw, deep_ref\r\n\r\ndata = [1, 2, 3]\r\ndata_ref = deep_ref(data)\r\n\r\nassert data_ref.value is not data\r\nassert to_raw(data_ref.value) is data\r\n```\r\n\r\n---\r\n\r\n### `effect`\r\nAccepts a function and automatically monitors changes to the responsive objects used in the function to automatically execute the function.\r\n\r\n```python\r\na = to_ref(1)\r\nb = to_ref(\"text\")\r\n\r\n\r\n@effect\r\ndef auto_run_when_ref_value():\r\n    print(f\"a:{a.value}\")\r\n\r\n\r\ndef change_value():\r\n    a.value = 2\r\n    b.value = \"new text\"\r\n\r\n\r\nui.button(\"change\", on_click=change_value)\r\n```\r\n\r\nThe first time the effect is executed, the function `auto_run_when_ref_value` will be executed once. After that, clicking on the button changes the value of `a` (via `a.value`) and the function `auto_run_when_ref_value` is executed again.\r\n\r\n> Never spread a lot of data processing logic across multiple `on`s or `effects`s, which should be mostly interface manipulation logic rather than responsive data processing logic\r\n\r\n---\r\n\r\n### `ref_computed`\r\nAs with `effect`, `ref_computed` can also return results from functions. Typically used for secondary computation from `to_ref`.\r\n\r\n```python\r\na = to_ref(1)\r\na_square = ref_computed(lambda: a.value * 2)\r\n\r\n\r\n@effect\r\ndef effect1():\r\n    print(f\"a_square:{a_square.value}\")\r\n\r\n\r\ndef change_value():\r\n    a.value = 2\r\n\r\n\r\nui.button(\"change\", on_click=change_value)\r\n```\r\n\r\nWhen the button is clicked, the value of `a.value` is modified, triggering a recalculation of `a_square`. As the value of `a_square` is read in `effect1`, it triggers `effect1` to execute the\r\n\r\n> `ref_computed` is read-only `to_ref`\r\n\r\nIf you prefer to organize your code by class, ``ref_computed`` also supports acting on instance methods\r\n\r\n```python\r\nclass MyState.\r\n    def __init__(self) -> None.\r\n        self.r_text = to_ref(\"\")\r\n\r\n    @ref_computed\r\n    def post_text(self): return self.r_text.value\r\n        return self.r_text.value + \"post\"\r\n\r\nstate = MyState()\r\n\r\nrxui.input(value=state.r_text)\r\nrxui.label(state.post_text)\r\n```\r\n\r\n---\r\n\r\n### `async_computed`\r\nUse `async_computed` when asynchronous functions are required for computed.\r\n\r\n```python\r\n\r\n# Simulate asynchronous functions that take a long time to execute\r\nasync def long_time_query(input: str):\r\n    await asyncio.sleep(2)\r\n    num = random.randint(20, 100)\r\n    return f\"query result[{input=}]:{num=}\"\r\n\r\n\r\nsearch = to_ref(\"\")\r\nevaluating = to_ref(False)\r\n\r\n@async_computed(search, evaluating=evaluating, init=\"\")\r\nasync def search_result():\r\n    return await long_time_query(search.value)\r\n\r\nrxui.lazy_input(value=search)\r\n\r\nrxui.label(\r\n    lambda: \"Query in progress\" if evaluating.value else \"Enter content in input box above and return to search\"\r\n)\r\nrxui.label(search_result)\r\n\r\n```\r\n\r\n- The first argument to `async_computed` must explicitly specify the responsive data to be monitored. Multiple responses can be specified using a list.\r\n- Parameter `evaluating` is responsive data of type bool, which is `True` while the asynchronous function is executing, and `False` after the computation is finished.\r\n- Parameter `init` specifies the initial result\r\n\r\n---\r\n\r\n### `on`\r\nSimilar to `effect`, but `on` needs to explicitly specify the responsive object to monitor.\r\n\r\n```python\r\n\r\na1 = to_ref(1)\r\na2 = to_ref(10)\r\nb = to_ref(\"text\")\r\n\r\n\r\n@on(a1)\r\ndef watch_a1_only():\r\n    print(f\"watch_a1_only ... a1:{a1.value},a2:{a2.value}\")\r\n\r\n\r\n@on([a1, b], onchanges=True)\r\ndef watch_a1_and_b():\r\n    print(f\"watch_a1_and_b ... a1:{a1.value},a2:{a2.value},b:{b.value}\")\r\n\r\n\r\ndef change_a1():\r\n    a1.value += 1\r\n    ui.notify(\"change_a1\")\r\n\r\n\r\nui.button(\"change a1\", on_click=change_a1)\r\n\r\n\r\ndef change_a2():\r\n    a2.value += 1\r\n    ui.notify(\"change_a2\")\r\n\r\n\r\nui.button(\"change a2\", on_click=change_a2)\r\n\r\n\r\ndef change_b():\r\n    b.value += \"x\"\r\n    ui.notify(\"change_b\")\r\n\r\n\r\nui.button(\"change b\", on_click=change_b)\r\n\r\n```\r\n\r\n- If the parameter `onchanges` is True (the default value is False), the specified function will not be executed at binding time.\r\n\r\n> Never spread a lot of data processing logic across multiple `on`s or `effects`s, which should be mostly interface manipulation logic rather than responsive data processing logic\r\n\r\n---\r\n\r\n### `new_scope`\r\n\r\nBy default, all watch functions are automatically destroyed when the client connection is disconnected. For finer-grained control, the `new_scope` function can be utilized.\r\n\r\n```python\r\nfrom nicegui import ui\r\nfrom ex4nicegui import rxui, to_ref, effect, new_scope\r\n\r\na = to_ref(0.0)\r\n\r\nscope1 = new_scope()\r\n\r\n@scope1.run\r\ndef _():\r\n    @effect\r\n    def _():\r\n        print(f\"scope 1:{a.value}\")\r\n\r\n\r\nrxui.number(value=a)\r\nrxui.button(\"dispose scope 1\", on_click=scope1.dispose)\r\n```\r\n\r\n---\r\n\r\n## functionality\r\n\r\n### vmodel\r\nCreate two-way bindings on form input elements or components.\r\n\r\nBidirectional bindings are supported by default for `ref` simple value types\r\n```python\r\nfrom ex4nicegui import rxui, to_ref, deep_ref\r\n\r\ndata = to_ref(\"init\")\r\n\r\nrxui.label(lambda: f\"{data.value=}\")\r\n# two-way binding \r\nrxui.input(value=data)\r\n```\r\n- Simple value types are generally immutable values such as `str`, `int`, etc.\r\n\r\nWhen using complex data structures, `deep_ref` is used to keep nested values responsive.\r\n```python\r\ndata = deep_ref({\"a\": 1, \"b\": [1, 2, 3, 4]})\r\n\r\nrxui.label(lambda: f\"{data.value=!s}\")\r\n\r\n# No binding effect\r\nrxui.input(value=data.value[\"a\"])\r\n\r\n# readonly binding\r\nrxui.input(value=lambda: data.value[\"a\"])\r\n\r\n# two-way binding\r\nrxui.input(value=rxui.vmodel(data.value[\"a\"]))\r\n```\r\n\r\n- The first input box will be completely unresponsive because the code is equivalent to passing in a value `1` directly\r\n- The second input box will get read responsiveness due to the use of a function.\r\n- The third input box, wrapped in `rxui.vmodel`, can be bi-directionally bound.\r\n\r\nMost use `vmodel` with `vfor`, see [todo list examples](./examples/todomvc/)\r\n\r\n---\r\n\r\n### vfor\r\nRender list components based on list responsive data. Each component is updated on demand. Data items support dictionaries or objects of any type\r\n\r\n```python\r\nfrom nicegui import ui\r\nfrom ex4nicegui.reactive import rxui\r\nfrom ex4nicegui import deep_ref, ref_computed\r\nfrom typing import Dict\r\n\r\n# refs\r\nitems = deep_ref(\r\n    [\r\n        {\"id\": 1, \"message\": \"foo\", \"done\": False},\r\n        {\"id\": 2, \"message\": \"bar\", \"done\": True},\r\n    ]\r\n)\r\n\r\n# ref_computeds\r\n@ref_computed\r\ndef done_count_info():\r\n    return f\"done count:{sum(item['done'] for item in items.value)}\"\r\n\r\n# method\r\ndef check():\r\n    for item in items.value:\r\n        item[\"done\"] = not item[\"done\"]\r\n\r\n\r\n# ui\r\nrxui.label(done_count_info)\r\nui.button(\"check\", on_click=check)\r\n\r\n\r\n@rxui.vfor(items,key='id')\r\ndef _(store: rxui.VforStore[Dict]):\r\n    # function to build the interface for each row of data\r\n    item = store.get()  # Get responsive object with `store.get`\r\n    mes = rxui.vmodel(item.value['message'])\r\n\r\n    # Enter the content of the input box, \r\n    # you can see the title of the radio box changes synchronously\r\n    with ui.card():\r\n        with ui.row():\r\n            rxui.input(value=mes)\r\n            rxui.label(lambda: f\"{mes.value=!s}\")\r\n        rxui.checkbox(text=mes, value=rxui.vmodel(item.value['done']))\r\n\r\n```\r\n\r\n- `rxui.vfor` decorator to custom function\r\n    - The first argument is passed to the responsive list. Each item in the list can be a dictionary or other object (`dataclasses` etc.)\r\n    - Second parameter `key`: In order to be able to keep track of the identity of each node, and thus reuse and reorder existing elements, you can provide a unique key for the block corresponding to each element. The default(`None`) is to use the list element index.\r\n- The custom function takes one argument. The current row's  can be retrieved via `store.get`, which is a responsive object.\r\n\r\n\r\n> vfor are created only when new data is added.\r\n\r\n\r\n---\r\n\r\n\r\n### Bind class names\r\n\r\nAll component classes provide `bind_classes` for binding `class`, supporting three different data structures.\r\n\r\nBind dictionaries\r\n\r\n```python\r\nbg_color = to_ref(False)\r\nhas_error = to_ref(False)\r\n\r\nrxui.label(\"test\").bind_classes({\"bg-blue\": bg_color, \"text-red\": has_error})\r\n\r\nrxui.switch(\"bg_color\", value=bg_color)\r\nrxui.switch(\"has_error\", value=has_error)\r\n```\r\n\r\nDictionary key  is the class name, and a dictionary value of  a responsive variable with value `bool`. When the responsive value is `True`, the class name is applied to the component `class`.\r\n\r\n---\r\n\r\nBind a responsive variable whose return value is a dictionary.\r\n\r\n```python\r\nbg_color = to_ref(False)\r\nhas_error = to_ref(False)\r\n\r\nclass_obj = ref_computed(\r\n    lambda: {\"bg-blue\": bg_color.value, \"text-red\": has_error.value}\r\n)\r\n\r\nrxui.switch(\"bg_color\", value=bg_color)\r\nrxui.switch(\"has_error\", value=has_error)\r\nrxui.label(\"bind to ref_computed\").bind_classes(class_obj)\r\n\r\n# or direct function passing\r\nrxui.label(\"bind to ref_computed\").bind_classes(\r\n    lambda: {\"bg-blue\": bg_color.value, \"text-red\": has_error.value}\r\n)\r\n```\r\n\r\n---\r\n\r\nBind to list\r\n\r\n```python\r\nbg_color = to_ref(\"red\")\r\nbg_color_class = ref_computed(lambda: f\"bg-{bg_color.value}\")\r\n\r\ntext_color = to_ref(\"green\")\r\ntext_color_class = ref_computed(lambda: f\"text-{text_color.value}\")\r\n\r\nrxui.select([\"red\", \"green\", \"yellow\"], label=\"bg color\", value=bg_color)\r\nrxui.select([\"red\", \"green\", \"yellow\"], label=\"text color\", value=text_color)\r\n\r\nrxui.label(\"binding to arrays\").bind_classes([bg_color_class, text_color_class])\r\n\r\n```\r\n\r\nEach element in the list is a responsive variable that returns the class name\r\n\r\n---\r\n\r\n### bind-style\r\n\r\n```python\r\nfrom nicegui import ui\r\nfrom ex4nicegui.reactive import rxui\r\nfrom ex4nicegui.utils.signals import to_ref\r\n\r\n\r\nbg_color = to_ref(\"blue\")\r\ntext_color = to_ref(\"red\")\r\n\r\nrxui.label(\"test\").bind_style(\r\n    {\r\n        \"background-color\": bg_color,\r\n        \"color\": text_color,\r\n    }\r\n)\r\n\r\nrxui.select([\"blue\", \"green\", \"yellow\"], label=\"bg color\", value=bg_color)\r\nrxui.select([\"red\", \"green\", \"yellow\"], label=\"text color\", value=text_color)\r\n```\r\n\r\n`bind_style` passed into dictionary, `key` is style name, `value` is style value, responsive string\r\n\r\n---\r\n\r\n### rxui.echarts\r\nCharting with echarts\r\n\r\n---\r\n\r\n#### rxui.echarts.from_javascript\r\nCreate echart from javascript code\r\n\r\n```python\r\nfrom pathlib import Path\r\n\r\nrxui.echarts.from_javascript(Path(\"code.js\"))\r\n# or\r\nrxui.echarts.from_javascript(\r\n    \"\"\"\r\n(myChart) => {\r\n\r\n    option = {\r\n        xAxis: {\r\n            type: 'category',\r\n            data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\r\n        },\r\n        yAxis: {\r\n            type: 'value'\r\n        },\r\n        series: [\r\n            {\r\n                data: [120, 200, 150, 80, 70, 110, 130],\r\n                type: 'bar'\r\n            }\r\n        ]\r\n    };\r\n\r\n    myChart.setOption(option);\r\n}\r\n\"\"\"\r\n)\r\n```\r\n\r\n- The first parameter of the function is the echart instance object. You need to configure the chart in the function with `setOption`.\r\n\r\n---\r\n\r\n#### rxui.echarts.register_map\r\nRegister a map.\r\n\r\n```python\r\nrxui.echarts.register_map(\r\n    \"china\", \"https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json\"\r\n)\r\n\r\nrxui.echarts(\r\n    {\r\n        \"geo\": {\r\n            \"map\": \"china\",\r\n            \"roam\": True,\r\n        },\r\n        \"tooltip\": {},\r\n        \"legend\": {},\r\n        \"series\": [], }\r\n    }\r\n)\r\n```\r\n\r\n- The parameter `map_name` is a customized map name. Note that `map` must correspond to a registered name in the chart configuration.\r\n- The parameter `src` is a valid network link to the map data.\r\n\r\nYou can also provide the path to the local json file for the map data.\r\n```python\r\nfrom pathlib import Path\r\n\r\nrxui.echarts.register_map(\r\n    \"china\", Path(\"map-data.json\")\r\n)\r\n```\r\n\r\n---\r\n\r\n### gsap\r\n\r\njs animation library. [gsap documentation](https://gsap.com/docs/v3/)\r\n\r\n```python\r\nfrom nicegui import ui\r\nfrom ex4nicegui import gsap\r\n```\r\n\r\n#### gsap.from_\r\n\r\nSet the start property, the animation will transition from the set property to the original position\r\n\r\n```python\r\n\r\nui.label(\"test from\").classes(\"target\")\r\ngsap.from_(\".target\", {\"x\": 50,'duration':1})\r\n\r\n```\r\n\r\nAfter the screen is loaded, the starting position of the text is shifted to the right by 50px, and then moved to the original position within 1 second.\r\n\r\n- Arguments `targets` are css selectors.\r\n- Arguments `vars` are attribute values\r\n\r\n---\r\n\r\n#### gsap.to\r\n\r\nSet the end property, the animation will transition from the original property to the set property.\r\n\r\n```python\r\n\r\nui.label(\"test to\").classes(\"target\")\r\ngsap.to(\".target\", {\"x\": 50,'duration':1})\r\n\r\n```\r\n\r\nAfter loading the screen, the text will be moved back 50px from the original position within 1 second.\r\n\r\n- Arguments `targets` are css selectors.\r\n- Arguments `vars` are attribute values\r\n\r\n---\r\n\r\n#### gsap.run_script\r\n\r\nSetting up animations by writing js\r\n\r\n```python\r\n\r\ngsap.run_script(\r\n            r\"\"\"function setGsap(gsap) {\r\n    gsap.to('.target',{\"duration\": 0.3,y:60})\r\n}\r\n\"\"\")\r\n```\r\n\r\n- The parameter `script` can be text or a file with a js extension `Path`.\r\n- The name of the defined js function doesn't matter, the first argument is a gsap object.\r\n\r\n---\r\n\r\n## BI Module\r\n\r\nCreate an interactive data visualization report using the minimal API.\r\n\r\n![](./asset/bi_examples1.gif)\r\n\r\n```python\r\nfrom nicegui import ui\r\nimport pandas as pd\r\nimport numpy as np\r\nfrom ex4nicegui import bi\r\nfrom ex4nicegui.reactive import rxui\r\nfrom ex4nicegui import effect, effect_refreshable\r\nfrom pyecharts.charts import Bar\r\n\r\n\r\n# data ready\r\ndef gen_data():\r\n    np.random.seed(265)\r\n    field1 = [\"a1\", \"a2\", \"a3\", \"a4\"]\r\n    field2 = [f\"name{i}\" for i in range(1, 11)]\r\n    df = (\r\n        pd.MultiIndex.from_product([field1, field2], names=[\"cat\", \"name\"])\r\n        .to_frame()\r\n        .reset_index(drop=True)\r\n    )\r\n    df[[\"idc1\", \"idc2\"]] = np.random.randint(50, 1000, size=(len(df), 2))\r\n    return df\r\n\r\n\r\ndf = gen_data()\r\n\r\n# Create a data source.\r\nds = bi.data_source(df)\r\n\r\n# ui\r\nui.query(\".nicegui-content\").classes(\"items-stretch no-wrap\")\r\n\r\nwith ui.row().classes(\"justify-evenly\"):\r\n    # Create components based on the data source `ds`.\r\n    ds.ui_select(\"cat\").classes(\"min-w-[10rem]\")\r\n    ds.ui_select(\"name\").classes(\"min-w-[10rem]\")\r\n\r\n\r\nwith ui.grid(columns=2):\r\n    # Configure the chart using a dictionary.\r\n    @ds.ui_echarts\r\n    def bar1(data: pd.DataFrame):\r\n        data = data.groupby(\"name\").agg({\"idc1\": \"sum\", \"idc2\": \"sum\"}).reset_index()\r\n\r\n        return {\r\n            \"xAxis\": {\"type\": \"value\"},\r\n            \"yAxis\": {\r\n                \"type\": \"category\",\r\n                \"data\": data[\"name\"].tolist(),\r\n                \"inverse\": True,\r\n            },\r\n            \"legend\": {\"textStyle\": {\"color\": \"gray\"}},\r\n            \"series\": [\r\n                {\"type\": \"bar\", \"name\": \"idc1\", \"data\": data[\"idc1\"].tolist()},\r\n                {\"type\": \"bar\", \"name\": \"idc2\", \"data\": data[\"idc2\"].tolist()},\r\n            ],\r\n        }\r\n\r\n    bar1.classes(\"h-[20rem]\")\r\n\r\n    # Configure the chart using pyecharts.\r\n    @ds.ui_echarts\r\n    def bar2(data: pd.DataFrame):\r\n        data = data.groupby(\"name\").agg({\"idc1\": \"sum\", \"idc2\": \"sum\"}).reset_index()\r\n\r\n        return (\r\n            Bar()\r\n            .add_xaxis(data[\"name\"].tolist())\r\n            .add_yaxis(\"idc1\", data[\"idc1\"].tolist())\r\n            .add_yaxis(\"idc2\", data[\"idc2\"].tolist())\r\n        )\r\n\r\n    bar2.classes(\"h-[20rem]\")\r\n\r\n    # Bind the click event to achieve navigation.\r\n    @bar2.on_chart_click\r\n    def _(e: rxui.echarts.EChartsMouseEventArguments):\r\n        ui.open(f\"/details/{e.name}\", new_tab=True)\r\n\r\n\r\n# with response mechanisms, you can freely combine native nicegui components.\r\nlabel_a1_total = ui.label(\"\")\r\n\r\n\r\n# this function will be triggered when ds changed.\r\n@effect\r\ndef _():\r\n    # prop `filtered_data` is the filtered DataFrame.\r\n    df = ds.filtered_data\r\n    total = df[df[\"cat\"] == \"a1\"][\"idc1\"].sum()\r\n    label_a1_total.text = f\"idc1 total(cat==a1):{total}\"\r\n\r\n\r\n# you can also use `effect_refreshable`, but you need to note that the components in the function are rebuilt each time.\r\n@effect_refreshable\r\ndef _():\r\n    df = ds.filtered_data\r\n    total = df[df[\"cat\"] == \"a2\"][\"idc1\"].sum()\r\n    ui.label(f\"idc1 total(cat==a2):{total}\")\r\n\r\n\r\n# the page to be navigated when clicking on the chart series.\r\n@ui.page(\"/details/{name}\")\r\ndef details_page(name: str):\r\n    ui.label(\"This table data will not change\")\r\n    ui.aggrid.from_pandas(ds.data.query(f'name==\"{name}\"'))\r\n\r\n    ui.label(\"This table will change when the homepage data changes. \")\r\n\r\n    @bi.data_source\r\n    def new_ds():\r\n        return ds.filtered_data[[\"name\", \"idc1\", \"idc2\"]]\r\n\r\n    new_ds.ui_aggrid()\r\n\r\n\r\nui.run()\r\n```\r\n\r\n### Details\r\n\r\n#### `bi.data_source`\r\nThe data source is the core concept of the BI module, and all data linkage is based on this. In the current version (0.4.3), there are two ways to create a data source.\r\n\r\nReceive `pandas`'s `DataFrame`:\r\n```python\r\nfrom nicegui import ui\r\nfrom ex4nicegui import bi\r\nimport pandas as pd\r\n\r\ndf = pd.DataFrame(\r\n    {\r\n        \"name\": list(\"aabcdf\"),\r\n        \"cls\": [\"c1\", \"c2\", \"c1\", \"c1\", \"c3\", None],\r\n        \"value\": range(6),\r\n    }\r\n)\r\n\r\nds =  bi.data_source(df)\r\n```\r\n\r\nSometimes, we want to create a new data source based on another data source, in which case we can use a decorator to create a linked data source:\r\n```python\r\ndf = pd.DataFrame(\r\n    {\r\n        \"name\": list(\"aabcdf\"),\r\n        \"cls\": [\"c1\", \"c2\", \"c1\", \"c1\", \"c3\", None],\r\n        \"value\": range(6),\r\n    }\r\n)\r\n\r\nds =  bi.data_source(df)\r\n\r\n@bi.data_source\r\ndef new_ds():\r\n    # df is pd.DataFrame \r\n    df = ds.filtered_data\r\n    df=df.copy()\r\n    df['value'] = df['value'] * 100\r\n    return df\r\n\r\nds.ui_select('name')\r\nnew_ds.ui_aggrid()\r\n```\r\n\r\nNote that since `new_ds` uses `ds.filtered_data`, changes to `ds` will trigger the linkage change of `new_ds`, causing the table component created by `new_ds` to change.\r\n\r\n---\r\n\r\nRemove all filter states through the `ds.remove_filters` method:\r\n```python\r\nds = bi.data_source(df)\r\n\r\ndef on_remove_filters():\r\n    ds.remove_filters()\r\n\r\nui.button(\"remove all filters\", on_click=on_remove_filters)\r\n\r\nds.ui_select(\"name\")\r\nds.ui_aggrid()\r\n```\r\n---\r\n\r\nReset the data source through the `ds.reload` method:\r\n```python\r\n\r\ndf = pd.DataFrame(\r\n    {\r\n        \"name\": list(\"aabcdf\"),\r\n        \"cls\": [\"c1\", \"c2\", \"c1\", \"c1\", \"c3\", None],\r\n        \"value\": range(6),\r\n    }\r\n)\r\n\r\nnew_df = pd.DataFrame(\r\n    {\r\n        \"name\": list(\"xxyyds\"),\r\n        \"cls\": [\"cla1\", \"cla2\", \"cla3\", \"cla3\", \"cla3\", None],\r\n        \"value\": range(100, 106),\r\n    }\r\n)\r\n\r\nds = bi.data_source(df)\r\n\r\ndef on_remove_filters():\r\n    ds.reload(new_df)\r\n\r\nui.button(\"reload data\", on_click=on_remove_filters)\r\n\r\nds.ui_select(\"name\")\r\nds.ui_aggrid()\r\n```\r\n\r\n---\r\n#### ui_select\r\n\r\nDropdown Select Box\r\n\r\n```python\r\nfrom nicegui import ui\r\nfrom ex4nicegui import bi\r\nimport pandas as pd\r\n\r\ndf = pd.DataFrame(\r\n    {\r\n        \"name\": list(\"aabcdf\"),\r\n        \"cls\": [\"c1\", \"c2\", \"c1\", \"c1\", \"c3\", None],\r\n        \"value\": range(6),\r\n    }\r\n)\r\n\r\nds = bi.data_source(df)\r\n\r\nds.ui_select(\"name\")\r\n```\r\n\r\nThe first parameter column specifies the column name of the data source.\r\n\r\n---\r\nSet the order of options using the parameter `sort_options`:\r\n```python\r\nds.ui_select(\"name\", sort_options={\"value\": \"desc\", \"name\": \"asc\"})\r\n```\r\n\r\n---\r\nSet whether to exclude null values using the parameter `exclude_null_value`:\r\n```python\r\ndf = pd.DataFrame(\r\n    {\r\n        \"cls\": [\"c1\", \"c2\", \"c1\", \"c1\", \"c3\", None],\r\n    }\r\n)\r\n\r\nds = bi.data_source(df)\r\nds.ui_select(\"cls\", exclude_null_value=True)\r\n```\r\n\r\n---\r\nYou can set the parameters of the native nicegui select component through keyword arguments.\r\n\r\nSet default values through the value attribute:\r\n```python\r\nds.ui_select(\"cls\",value=['c1','c2'])\r\nds.ui_select(\"cls\",multiple=False,value='c1')\r\n```\r\nFor multiple selections (the parameter `multiple` is defaulted to True), `value` needs to be specified as a list. For single selections, `value` should be set to non-list.\r\n\r\n---\r\n\r\n\r\n#### ui_table\r\n\r\nTable\r\n\r\n```python\r\nfrom nicegui import ui\r\nfrom ex4nicegui import bi\r\nimport pandas as pd\r\n\r\ndata = pd.DataFrame({\"name\": [\"f\", \"a\", \"c\", \"b\"], \"age\": [1, 2, 3, 1]})\r\nds = bi.data_source(data)\r\n\r\nds.ui_table(\r\n    columns=[\r\n        {\"label\": \"new colA\", \"field\": \"colA\", \"sortable\": True},\r\n    ]\r\n)\r\n\r\n```\r\n\r\n- The parameter `columns` are consistent with nicegui `ui.table`. The key value `field` corresponds to the column name of the data source, and if it does not exist, this configuration will not take effect\r\n- The `rows` parameter will not take effect. Because the data source of the table is always controlled by the data source.\r\n\r\n---\r\n\r\n#### ui_aggrid\r\n\r\n\r\n```python\r\nfrom nicegui import ui\r\nfrom ex4nicegui import bi\r\nimport pandas as pd\r\n\r\ndata = pd.DataFrame(\r\n    {\r\n        \"colA\": list(\"abcde\"),\r\n        \"colB\": [f\"n{idx}\" for idx in range(5)],\r\n        \"colC\": list(range(5)),\r\n    }\r\n)\r\ndf = pd.DataFrame(data)\r\n\r\nsource = bi.data_source(df)\r\n\r\nsource.ui_aggrid(\r\n    options={\r\n        \"columnDefs\": [\r\n            {\"headerName\": \"xx\", \"field\": \"no exists\"},\r\n            {\"headerName\": \"new colA\", \"field\": \"colA\"},\r\n            {\r\n                \"field\": \"colC\",\r\n                \"cellClassRules\": {\r\n                    \"bg-red-300\": \"x < 3\",\r\n                    \"bg-green-300\": \"x >= 3\",\r\n                },\r\n            },\r\n        ],\r\n        \"rowData\": [{\"colX\": [1, 2, 3, 4, 5]}],\r\n    }\r\n)\r\n```\r\n\r\n- The parameter `options` is consistent with nicegui `ui.aggrid`. The key value `field` in columnDefs corresponds to the column name of the data source, and if it does not exist, this configuration will not take effect.\r\n- The `rowData` key value will not take effect. Because the data source of the table is always controlled by the data source.\r\n\r\n\r\n",
    "bugtrack_url": null,
    "license": "MIT license",
    "summary": "Extension library based on nicegui, providing data responsive,BI functionality modules",
    "version": "0.6.6",
    "project_urls": null,
    "split_keywords": [
        "nicegui",
        " ex4nicegui",
        " webui"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "1d9672d59aa110f532387836db4c3f4a7ee6bda0ec9e1048368d77c955aae8d0",
                "md5": "7870e4790e8d76b4d6e2b6a090847ba4",
                "sha256": "5cb268ac3e4dae1da6da05fd650f5aeab3fefe206195a4f71ab988a24c7da679"
            },
            "downloads": -1,
            "filename": "ex4nicegui-0.6.6-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "7870e4790e8d76b4d6e2b6a090847ba4",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 341686,
            "upload_time": "2024-05-15T16:43:04",
            "upload_time_iso_8601": "2024-05-15T16:43:04.657121Z",
            "url": "https://files.pythonhosted.org/packages/1d/96/72d59aa110f532387836db4c3f4a7ee6bda0ec9e1048368d77c955aae8d0/ex4nicegui-0.6.6-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "2235cf3aa27af3105d619e449cb9ecd642e66f51e4ab65ef21553c3c3d5a6178",
                "md5": "0325249ab251665a845328952ea1d0b8",
                "sha256": "e8486b1eb06167efa65591bd99c98582922d68c0c3024a24867190b71269f0a5"
            },
            "downloads": -1,
            "filename": "ex4nicegui-0.6.6.tar.gz",
            "has_sig": false,
            "md5_digest": "0325249ab251665a845328952ea1d0b8",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 295193,
            "upload_time": "2024-05-15T16:43:08",
            "upload_time_iso_8601": "2024-05-15T16:43:08.638645Z",
            "url": "https://files.pythonhosted.org/packages/22/35/cf3aa27af3105d619e449cb9ecd642e66f51e4ab65ef21553c3c3d5a6178/ex4nicegui-0.6.6.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-05-15 16:43:08",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "ex4nicegui"
}
        
Elapsed time: 0.28171s