単純化された中国人|英語
Nickguiの拡張ライブラリ。組み込みのレスポンシブコンポーネント、データレスポンシブインターフェイスプログラミングを完全に実装します。
より多くの例を参照してください
見出し記事 - フラッシュ販売の公式実装、Pythonインターフェイスライブラリ、90%のイベントコードを削除したNiceGui
WECHAT公式アカウント - フラッシュセールの公式実装、Pythonインターフェイスライブラリ、90%のイベントコードを削除したNICEGUI
pip install ex4nicegui -U
ボタンをクリックすると、ユーザーがカウントを増やすか減少させることができる単純なカウンターアプリケーションから始めます。
これが完全なコードです:
from nicegui import ui
from ex4nicegui import rxui
# 数据状态代码
class Counter ( rxui . ViewModel ):
count : int = 0
def increment ( self ):
self . count += 1
def decrement ( self ):
self . count -= 1
# 界面代码
counter = Counter ()
with ui . row ( align_items = "center" ):
ui . button ( icon = "remove" , on_click = counter . decrement )
rxui . label ( counter . count )
ui . button ( icon = "add" , on_click = counter . increment )
ui . run ()今すぐ詳細をご覧ください。 ex4nicegui 、データ駆動型の方法に従ってインターフェイスを定義します。ステータスデータは、アプリケーションで変更できるすべてのデータを定義します。
以下はCounterステータスデータ定義です。
class Counter ( rxui . ViewModel ):
count : int = 0rxui.ViewModelを継承する必要がありますcountで、初期値は0です。次に、クラス内のデータを操作するための一連の方法を定義します。
def increment ( self ):
self . count += 1
def decrement ( self ):
self . count -= 1count変数の値を変更できるインスタンスメソッドです次に、インターフェイスコードで、 Counterオブジェクトをインスタンス化します。
counter = Counter () rxui.labelコンポーネントを介してcount変数をバインドします。データを操作する方法をボタンクリックイベントにバインドします。
ui . button ( icon = "remove" , on_click = counter . decrement )
rxui . label ( counter . count )
ui . button ( icon = "add" , on_click = counter . increment )nicegui Namespaceの下のlabelコンポーネントではなく、 rxuiネームスペースの下のlabelコンポーネントを使用する必要があります。rxui.labelコンポーネントはcounter.count変数にバインドし、 counter.countが変更されると、 rxui.labelコンポーネントが自動的に更新されます。ui.buttonコンポーネントはcounter.decrement 、削除およびcounter.incrementメソッドにバインドし、ボタンをクリックするときに対応するメソッドを呼び出します。複雑なプロジェクトでは、
Counter定義コードを個別のモジュールに配置し、インターフェイスコードにインポートできます。
クラス変数名の下線が付いている場合、データステータスは自動的に更新されないことに注意してください。
class Counter ( rxui . ViewModel ):
count : int = 0 # 响应式数据,能自动同步界面
_count : int = 0 # 这里的下划线表示私有变量,不会自动同步界面前の例の横に、別の関数を追加します。カウンター値が0未満の場合、フォントは赤で表示され、0を超えると緑色で表示され、それ以外の場合は黒で表示されます。
# 数据状态代码
class Counter ( rxui . ViewModel ):
count : int = 0
def text_color ( self ):
if self . count > 0 :
return "green"
elif self . count < 0 :
return "red"
else :
return "black"
def increment ( self ):
self . count += 1
def decrement ( self ):
self . count -= 1
# 界面代码
counter = Counter ()
with ui . row ( align_items = "center" ):
ui . button ( icon = "remove" , on_click = counter . decrement )
rxui . label ( counter . count ). bind_color ( counter . text_color )
ui . button ( icon = "add" , on_click = counter . increment )色の値は、カウンターの現在の値に基づいて計算されます。二次計算に属します。通常のインスタンス関数を定義するだけです。
def text_color ( self ):
if self . count > 0 :
return "green"
elif self . count < 0 :
return "red"
else :
return "black"次に、 text_colorメソッドは、 rxui.labelコンポーネントのbind_colorメソッドにバインドされているため、色値が自動的に更新されます。
rxui . label ( counter . count ). bind_color ( counter . text_color )次に、カウンターの下のテキストを使用して、現在のカウンターの色のテキスト値を表示します。
...
# 数据状态代码
class Counter ( rxui . ViewModel ):
...
# 界面代码
counter = Counter ()
with ui . row ( align_items = "center" ):
ui . button ( icon = "remove" , on_click = counter . decrement )
rxui . label ( counter . count ). bind_color ( counter . text_color )
ui . button ( icon = "add" , on_click = counter . increment )
rxui . label ( lambda : f"当前计数器值为 { counter . count } , 颜色值为 { counter . text_color () } " )上記のコードには、 counter.text_colorメソッドを使用する2つの場所があります。 counter.countが変更されると、 counter.text_color 2つの計算を実行します。 2番目の計算は冗長です。
不必要な計算を避けるために、 counter.text_colorをキャッシュできます。
# 数据状态代码
class Counter ( rxui . ViewModel ):
count : int = 0
@ rxui . cached_var
def text_color ( self ):
if self . count > 0 :
return "green"
elif self . count < 0 :
return "red"
else :
return "black"rxui.cached_varデコレーターは、関数の結果をキャッシュして、不必要な計算を避けることができます。次の例は、リストの使用方法を示しています。
class AppState ( rxui . ViewModel ):
nums = []
# nums = [1,2,3] 如果需要初始化,必须在 __init__ 中设置
def __init__ ( self ):
super (). __init__ ()
self . nums = [ 1 , 2 , 3 ]
def append ( self ):
new_num = max ( self . nums ) + 1
self . nums . append ( new_num )
def pop ( self ):
self . nums . pop ()
def reverse ( self ):
self . nums . reverse ()
def display_nums ( self ):
return ", " . join ( map ( str , self . nums ))
# 界面代码
state = AppState ()
with ui . row ( align_items = "center" ):
ui . button ( "append" , on_click = state . append )
ui . button ( "pop" , on_click = state . pop )
ui . button ( "reverse" , on_click = state . reverse )
rxui . label ( state . display_nums )リストを定義するときにリストを初期化する必要がある場合は、 __init__に設定することをお勧めします。
class AppState ( rxui . ViewModel ):
nums = []
# nums = [1,2,3] 如果需要初始化,必须在 __init__ 中设置
def __init__ ( self ):
super (). __init__ ()
self . nums = [ 1 , 2 , 3 ]
...別の方法は、 rxui.list_var使用することです
class AppState ( rxui . ViewModel ):
# nums = []
# nums = [1,2,3] 如果需要初始化,必须在 __init__ 中设置
nums = rxui . list_var ( lambda : [ 1 , 2 , 3 ])
...rxui.list_varパラメーターは、リストを返す関数ですリストを定義した後、 effect_refreshable.on decoratorを使用して、インターフェイスにリストデータを表示できます。
次の例では、インターフェイスは、ドロップダウンボックスに選択されたアイコンを動的に表示します。
from ex4nicegui import rxui , effect_refreshable
class AppState ( rxui . ViewModel ):
icons = []
_option_icons = [ "font_download" , "warning" , "format_size" , "print" ]
state = AppState ()
# 界面代码
with ui . row ( align_items = "center" ):
@ effect_refreshable . on ( state . icons )
def _ ():
for icon in state . icons :
ui . icon ( icon , size = "2rem" )
rxui . select ( state . _option_icons , value = state . icons , multiple = True )その中で、 @effect_refreshable.on(state.icons)依存関係を明示的に指定します。 state.iconsが変更されると、 _関数は再実行されます。
@ effect_refreshable . on ( state . icons )
def _ ():
# 这里的代码会在 state.icons 变化时重新执行
...実行するたびに、内部のコンテンツがクリアされることに注意してください。これは、
ui.refreshableのデータ駆動型バージョンです
原則として、監視対象のデータは、 .on指定せずに自動的に監視できます。
@ effect_refreshable # 没有使用 .on(state.icons)
def _ ():
# 这里读取了 state.icons,因此会自动监控
for icon in state . icons :
ui . icon ( icon , size = "2rem" )予期しない更新を避けるために、常に依存関係を指定することをお勧めします
.on
ViewModelプロキシオブジェクトを使用して、レスポンシブデータを作成します。データを保存する必要がある場合は、 rxui.ViewModel.to_valueを使用して通常のデータに変換できます。
次の例では、ボタンをクリックすると、My_Appのステータスデータ辞書が表示されます。
from nicegui import ui
from ex4nicegui import rxui
class MyApp ( rxui . ViewModel ):
a = 0
sign = "+"
b = 0
def show_data ( self ):
# >> {"a": 0, "sign": '+, "b": 0}
return rxui . ViewModel . to_value ( self )
def show_a ( self ):
# >> 0
return rxui . ViewModel . to_value ( self . a )
my_app = MyApp ()
rxui . number ( value = my_app . a , min = 0 , max = 10 )
rxui . radio ([ "+" , "-" , "*" , "/" ], value = my_app . sign )
rxui . number ( value = my_app . b , min = 0 , max = 10 )
ui . button ( "show data" , on_click = lambda : ui . notify ( my_app . show_data ())) rxui.ViewModel.on_refs_changedと組み合わせて、データが変更されたときにデータをローカルエリアに自動的に保存できます。
from nicegui import ui
from ex4nicegui import rxui
from pathlib import Path
import json
class MyApp ( rxui . ViewModel ):
a = 0
sign = "+"
b = 0
_json_path = Path ( __file__ ). parent / "data.json"
def __init__ ( self ):
super (). __init__ ()
@ rxui . ViewModel . on_refs_changed ( self )
def _ ():
# a, sign, b 任意一个值变化时,自动保存到本地
self . _json_path . write_text ( json . dumps ( self . show_data ()))
def show_data ( self ):
return rxui . ViewModel . to_value ( self )
...to_refdeep_refeffectref_computedasync_computedonnew_scopebi.data_source v0.7.0では、Responsiveデータのセットを管理するためにViewModelクラスが導入されました。
これが簡単な計算機の例です。
from ex4nicegui import rxui
class Calculator ( rxui . ViewModel ):
num1 = 0
sign = "+"
num2 = 0
@ rxui . cached_var
def result ( self ):
# 当 num1,sign,num2 任意一个值发生变化时,result 也会重新计算
return eval ( f" { self . num1 } { self . sign } { self . num2 } " )
# 每个对象拥有独立的数据
calc = Calculator ()
with ui . row ( align_items = "center" ):
rxui . number ( value = calc . num1 , label = "Number 1" )
rxui . select ( value = calc . sign , options = [ "+" , "-" , "*" , "/" ], label = "Sign" )
rxui . number ( value = calc . num2 , label = "Number 2" )
ui . label ( "=" )
rxui . label ( calc . result ). bind_color (
lambda : "red" if calc . result () < 0 else "black"
)次の例では、各人がカードを使用して表示します。上部は、すべての人の平均年齢を示しています。個人が平均年齢よりも古い場合、カードの外側の境界は赤くなります。 numberコンポーネントを介して年齢を変更すると、すべてが自動的に更新されます。
from typing import List
from ex4nicegui import rxui
from itertools import count
from nicegui import ui
id_generator = count ()
class Person ( rxui . ViewModel ):
name = ""
age = 0
def __init__ ( self , name : str , age : int ):
super (). __init__ ()
self . name = name
self . age = age
self . id = next ( id_generator )
class Home ( rxui . ViewModel ):
persons : List [ Person ] = []
deleted_person_index = 0
@ rxui . cached_var
def avg_age ( self ) -> float :
if len ( self . persons ) == 0 :
return 0
return round ( sum ( p . age for p in self . persons ) / len ( self . persons ), 2 )
def avg_name_length ( self ):
if len ( self . persons ) == 0 :
return 0
return round ( sum ( len ( p . name ) for p in self . persons ) / len ( self . persons ), 2 )
def delete_person ( self ):
if self . deleted_person_index < len ( self . persons ):
del self . persons [ int ( self . deleted_person_index )]
def sample_data ( self ):
self . persons = [
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 () } " )
rxui . label ( lambda : f"平均名字长度: { home . avg_name_length () } " )
rxui . number (
value = home . deleted_person_index , min = 0 , max = lambda : len ( home . persons ) - 1 , step = 1
)
ui . button ( "删除" , on_click = home . delete_person )
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 > home . avg_age (),
}
):
rxui . input ( value = person . name , placeholder = "名字" )
rxui . number ( value = person . age , min = 1 , max = 100 , step = 1 , placeholder = "年龄" )
ui . run () rxui.vforコードが複雑すぎると思われる場合は、代わりにeffect_refreshableデコレーターを使用できます。
from ex4nicegui import rxui , Ref , effect_refreshable
...
# 明确指定监控 home.persons 变化,可以避免意外刷新
@ effect_refreshable . on ( home . persons )
def _ ():
for person in home . persons . value :
...
rxui . number ( value = person . age , min = 1 , max = 100 , step = 1 , placeholder = "年龄" )
... home.persons変更(要素の追加または削除など)をリストするたびに、 effect_refreshableによって装飾された関数が再測定されることに注意してください。すべての要素が再現されることを意味します。
より複雑なアプリケーションについては、例を表示できます
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 )効果が最初に実行されると、関数auto_run_when_ref_value 1回実行されます。その後、ボタンをクリックして( a.valueを介して)のaを変更すると、関数auto_run_when_ref_valueが再度実行されます。
多数のデータ処理ロジックを複数または
effecton分散しないでください。onのほとんどはeffect応答性のあるデータ処理ロジックではなく、インターフェイス操作ロジックである必要があります。
ref_computed effectと同じ関数で、 ref_computed関数の結果を返すこともできます。一般に、 to_refからの2次計算を実行するために使用されます
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再計算がトリガーされます。 a_squareの値はeffect1が読み取られるため、 effect1実行がトリガーされます。
ref_computedは読み取り専用to_refです
v0.7.0から始めて、 ref_computedアプリケーションインスタンスメソッドを使用することはお勧めしません。 rxui.ViewModelを使用して、 rxui.cached_varデコレーターを使用できます
class MyState ( rxui . ViewModel ):
def __init__ ( self ) -> None :
self . r_text = to_ref ( "" )
@ rxui . cached_var
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パラメーターは、ブールタイプの応答性データです。非同期関数が実行されると、この変数の値は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)の場合、指定された関数はバインディング中に実行されません。多数のデータ処理ロジックを複数または
effecton分散しないでください。onのほとんどはeffect応答性のあるデータ処理ロジックではなく、インターフェイス操作ロジックである必要があります。
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 )フォーム入力要素またはコンポーネントに双方向バインディングを作成します。
シンプルな値タイプの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 , "a" ))
# 也可以直接使用,但不推荐
rxui . input ( value = rxui . vmodel ( data . value [ 'a' ]))rxui.input(value=1)に相当するため、最初の入力ボックスは完全に反応しませんrxui.vmodelでラップされた3番目の入力ボックスは、双方向の結合を実現できます
rxui.ViewModelを使用している場合、おそらくvmodelを使用する必要はありません
TODOリストケースを参照できます
リストレスポンシブデータに基づいてリストコンポーネントをレンダリングします。各コンポーネントはオンデマンドで更新されます。データ項目は、あらゆるタイプの辞書またはオブジェクトをサポートしています。
v0.7.0から始めて、 rxui.ViewModelで使用することをお勧めします。 effect_refreshableデコレーターを使用するのとは異なり、 vforすべての要素を再現するのではなく、既存の要素を更新します。
以下はカードの並べ替えの例です。カードは常に年齢ごとにソートされます。カードの年齢データを変更すると、カードはリアルタイムで注文を調整します。ただし、カーソルフォーカスは入力ボックスを離れません。
from typing import List
from nicegui import ui
from ex4nicegui import rxui , deep_ref as ref , Ref
class Person ( rxui . ViewModel ):
def __init__ ( self , name : str , age : int ) -> None :
self . name = name
self . age = ref ( age )
class MyApp ( rxui . ViewModel ):
persons : Ref [ List [ Person ]] = rxui . var ( lambda : [])
order = rxui . var ( "asc" )
def sort_by_age ( self ):
return sorted (
self . persons . value ,
key = lambda p : p . age . value ,
reverse = self . order . value == "desc" ,
)
@ staticmethod
def create ():
persons = [
Person ( name = "Alice" , age = 25 ),
Person ( name = "Bob" , age = 30 ),
Person ( name = "Charlie" , age = 20 ),
Person ( name = "Dave" , age = 35 ),
Person ( name = "Eve" , age = 28 ),
]
app = MyApp ()
app . persons . value = persons
return app
# ui
app = MyApp . create ()
with rxui . tabs ( app . order ):
rxui . tab ( "asc" , "Ascending" )
rxui . tab ( "desc" , "Descending" )
@ rxui . vfor ( app . sort_by_age , key = "name" )
def each_person ( s : rxui . VforStore [ Person ]):
person = s . get_item ()
with ui . card (), ui . row ( align_items = "center" ):
rxui . label ( person . name )
rxui . number ( value = person . age , step = 1 , min = 0 , max = 100 )rxui.vforデコレータからカスタム機能app.sort_by_ageを呼び出す必要はないことに注意してくださいkey :各ノードのIDを追跡し、既存の要素を再利用および再注文するために、各要素の対応するブロックに一意のキーを提供できます。リスト要素インデックスはデフォルトで使用されます。この例では、全員の名前が一意であると想定されています。store.get_itemから取得できます。人自体はrxui.ViewModelから継承するため、そのさまざまな特性をコンポーネントに直接結合できます。 すべてのコンポーネントクラスは、バインディングclassにbind_classesを提供し、3つの異なるデータ構造をサポートします。
バインド辞書
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場合、クラス名はコンポーネントクラスに適用されます
辞書として返品値を持つレスポンシブ変数をバインドします
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 ) 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はスタイル値、レスポンシブストリングは
単一の属性をバインドします
label = to_ref ( "hello" )
rxui . button ( "" ). bind_prop ( "label" , label )
# 允许使用函数
rxui . button ( "" ). bind_prop (
"label" , lambda : f" { label . value } world"
)
rxui . input ( value = label )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 ()
on parameter event_nameとqueryを使用して、echarts events inspany documentを表示します
次の例では、マウスクリックイベントに結合します
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 ()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);
}
"""
)setOptionを介してチャート構成を完了する必要があります。この関数には2番目のパラメーターもあります。これは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 (
"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は、有効なMAPデータネットワークリンクです。 SVGデータの場合、パラメーターtype="svg"を設定する必要があります。
rxui . echarts . register_map ( "svg-rect" , "/test/svg" , type = "svg" )また、ローカルマップデータのJSONファイルパスオブジェクト(パス)を直接提供することもできます
from pathlib import Path
rxui . echarts . register_map (
"china" , Path ( "map-data.json" )
) nicegui.ui.tab_panelsと比較して、 rxui.tab_panelsはパラメーターtabsがありません。データレスポンシブメカニズムの下では、 tabsとtab_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 } " )レイジーロードモードでは、現在アクティブ化されているタブのみがレンダリングします。
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 _ ():
# 通过 `panels.get_panel` 获取当前激活的 panel 组件
panels . get_panel ( "t1" ). classes ( "bg-green" )
ui . notify ( "Hello from t1" )
ui . label ( "This is t1" )
@ panels . add_tab_panel ( "t2" )
def _ ():
panels . get_panel ( "t2" ). style ( "background-color : red" )
ui . notify ( "Hello from t2" )
ui . label ( "This is t2" )ページが読み込まれると、「T1からHello」がすぐに表示されます。 「T2」タブに切り替えると、「T2からHello」が表示されます。
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" )最も合理化されたAPIでインタラクティブなデータビジュアルレポートを作成する

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)には、データソースを作成する2つの方法があります。
pandasを受け取るDataFrame :
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 () ds.filtered_data new_dsで使用されるため、 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 () 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" )最初のパラメーター列は、データソースの列名を指定します
パラメーターsort_optionsでオプションの順序を設定します。
ds . ui_select ( "name" , sort_options = { "value" : "desc" , "name" : "asc" })パラメーターexclude_null_value null値を除外するかどうかを設定します。
df = pd . DataFrame (
{
"cls" : [ "c1" , "c2" , "c1" , "c1" , "c3" , None ],
}
)
ds = bi . data_source ( df )
ds . ui_select ( "cls" , exclude_null_value = True )キーワードパラメーターを使用して、ネイティブNiceGui選択コンポーネントのパラメーターを設定できます。
値属性を使用してデフォルト値を設定します。
ds . ui_select ( "cls" , value = [ 'c1' , 'c2' ])
ds . ui_select ( "cls" , multiple = False , value = 'c1' )複数の選択(デフォルトではパラメーターmultipleがtrue)の場合、 valueをリストとして指定する必要があります
valueを選択するときは、非リストに設定します
シート
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 },
]
)ui.tableと一致しています。キー値field 、データソースの列名に対応しています。それが存在しない場合、構成は有効になりません。 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 ]}],
}
)ui.aggridと一致しています。 columnDefsのキー値field 、データソースの列名に対応しています。それが存在しない場合、構成は有効になりません。rowDataキー値は有効になりません。テーブルのデータソースは常にデータソースによって制御されているためtoolboxモジュールは、一般的に使用されるツール機能を提供します。
from ex4nicegui import toolbox ダークモードを切り替えます
from ex4nicegui import rxui , toolbox as tb
from nicegui import ui
dark = tb . use_dark ( False )
rxui . label ( lambda : f"暗模式: { dark . value } " )
rxui . button (
icon = lambda : "sunny" if dark . value else "dark_mode" ,
color = lambda : "red" if dark . value else "blue" ,
on_click = dark . toggle ,
). props ( "flat round" )レスポンシブブレークポイント
from ex4nicegui import rxui , toolbox as tb
from nicegui import ui
options = { "手机" : 0 , "平板" : 640 , "笔记本" : 1024 , "桌面" : 1280 }
bp = tb . use_breakpoints ( options )
active = bp . active
is_between = bp . between ( "手机" , "笔记本" )
with ui . card ():
rxui . label ( lambda : f"当前断点: { active . value } " )
rxui . label ( lambda : f"是否在手机-笔记本(不含)之间: { is_between . value } " ). bind_classes (
{ "text-red-500" : is_between }
)
rxui . label ( lambda : f'手机(0px - 640px): { active . value == "手机" } ' ). bind_classes (
{ "bg-red-300" : lambda : active . value == "手机" }
)
rxui . label ( lambda : f'平板(640px - 1024px): { active . value == "平板" } ' ). bind_classes (
{ "bg-red-300" : lambda : active . value == "平板" }
)
rxui . label (
lambda : f'笔记本(1024px - 1280px): { active . value == "笔记本" } '
). bind_classes ({ "bg-red-300" : lambda : active . value == "笔记本" })
rxui . label ( lambda : f'桌面(1280px+): { active . value == "桌面" } ' ). bind_classes (
{ "bg-red-300" : lambda : active . value == "桌面" }
)
QRコードを生成します
from ex4nicegui import rxui , to_ref , toolbox as tb
from nicegui import ui
text = to_ref ( "ex4nicegui" )
qr_code = tb . use_qr_code ( text )
rxui . input ( value = text )
rxui . image ( qr_code . code ). classes ( "w-20 h-20" ). props ( "no-transition" )