تبسيط الصينية | إنجليزي
مكتبة التمديد لـ NiceGui. مكونات مستجيبة مدمجة ، وتنفيذ البيانات بالكامل برمجة واجهة الاستجابة للبيانات.
رؤية المزيد من الأمثلة
مقالة العنوان - التنفيذ الرسمي لبيع الفلاش ، مكتبة واجهة Python ، NiceGui مع إزالة رمز الحدث بنسبة 90 ٪
حساب WeChat الرسمي - التنفيذ الرسمي لبيع الفلاش ، مكتبة واجهة Python ، NiceGui مع إزالة رمز الحدث بنسبة 90 ٪
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.ViewModelcount متغير ، والذي يمثل القيمة الحالية للعداد ، بقيمة أولية قدرها 0.بعد ذلك ، حدد سلسلة من طرق معالجة البيانات في الفصل:
def increment ( self ):
self . count += 1
def decrement ( self ):
self . count -= 1count ثم ، في رمز الواجهة ، إنشاء كائن Counter .
counter = Counter () نربط متغير count من خلال مكون rxui.label . ربط طريقة معالجة البيانات إلى الحدث النقر فوق الزر.
ui . button ( icon = "remove" , on_click = counter . decrement )
rxui . label ( counter . count )
ui . button ( icon = "add" , on_click = counter . increment )label ضمن مساحة اسم rxui ، وليس مكون label تحت مساحة اسم nicegui .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 من خلال طريقة bind_color لمكون rxui.label ، بحيث يتم تحديث قيمة اللون تلقائيًا.
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 . عندما يتغير counter.count ، يقوم counter.text_color بإجراء حسابين. الحساب الثاني زائدة عن الحاجة.
لتجنب الحسابات غير الضرورية ، يمكننا Cache 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 Decorator ذاكرة التخزين المؤقت للوظيفة لتجنب الحسابات غير الضرورية.يوضح المثال التالي كيفية استخدام القائمة.
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 ، تم تقديم فئة 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 معقد للغاية ، فيمكنك استخدام Decorator 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يعادلto_refعندما يتم ضبطis_deepعلىTrue
deep_ref أي ما يعادل to_ref عندما يتم ضبط is_deep على True .
مفيد بشكل خاص عندما يكون مصدر البيانات قائمة أو قاموس أو فئة مخصصة. الكائنات التي تم الحصول عليها من خلال .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 مرة واحدة. بعد ذلك ، انقر فوق الزر وتغيير قيمة a (من خلال a.value ) ، وسيتم تنفيذ الوظيفة auto_run_when_ref_value مرة أخرى.
لا تقم بتشتيت كمية كبيرة من منطق معالجة البيانات في
onأوeffect. يجب أن يكون معظمonأوeffectهو منطق تشغيل الواجهة ، بدلاً من منطق معالجة البيانات الاستجابة.
ref_computed مع نفس الوظيفة مثل effect ، يمكن ref_computed أيضًا إرجاع النتيجة من الوظيفة. تستخدم بشكل عام لإجراء الحسابات التربيعية من to_ref
a = to_ref ( 1 )
a_square = ref_computed ( lambda : a . value * 2 )
@ effect
def effect1 ():
print ( f"a_square: { a_square . value } " )
def change_value ():
a . value = 2
ui . button ( "change" , on_click = change_value ) بعد النقر فوق الزر ، يتم تعديل قيمة a.value ، مما يؤدي إلى إعادة حساب a_square . نظرًا لقراءة قيمة a_square في effect1 ، يتم تشغيل تنفيذ effect1 .
ref_computedهو القراءة فقطto_ref
بدءًا من v0.7.0 ، لا ينصح باستخدام طريقة مثيل التطبيق ref_computed . يمكنك استخدام rxui.ViewModel واستخدام rxui.cached_var Decorator
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 هي بيانات استجابة من نوع Bool. عند تنفيذ الوظيفة غير المتزامنة ، تكون قيمة هذا المتغير True ، وهي False بعد اكتمال الحساب.init تحدد النتيجة الأولية on على غرار effect ، ولكن on يحتاج إلى تحديد كائن استجابة المراقبة
a1 = to_ref ( 1 )
a2 = to_ref ( 10 )
b = to_ref ( "text" )
@ on ( a1 )
def watch_a1_only ():
print ( f"watch_a1_only ... a1: { a1 . value } ,a2: { a2 . value } " )
@ on ([ a1 , b ], onchanges = True )
def watch_a1_and_b ():
print ( f"watch_a1_and_b ... a1: { a1 . value } ,a2: { a2 . value } ,b: { b . value } " )
def change_a1 ():
a1 . value += 1
ui . notify ( "change_a1" )
ui . button ( "change a1" , on_click = change_a1 )
def change_a2 ():
a2 . value += 1
ui . notify ( "change_a2" )
ui . button ( "change a2" , on_click = change_a2 )
def change_b ():
b . value += "x"
ui . notify ( "change_b" )
ui . button ( "change b" , on_click = change_b )onchanges صحيحة (القيمة الافتراضية خاطئة) ، فلن يتم تنفيذ الوظيفة المحددة أثناء الربط.لا تقم بتشتيت كمية كبيرة من منطق معالجة البيانات في
onأوeffect. يجب أن يكون معظم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 ، تحقيق الربط ثنائي الاتجاهإذا كنت تستخدم
rxui.ViewModel، فربما لا تحتاج إلى استخدامvmodel
يمكنك الرجوع إلى حالة قائمة TODO
عرض مكونات قائمة بناء على قائمة البيانات المستجيبة. يتم تحديث كل مكون عند الطلب. تدعم عناصر البيانات القواميس أو الكائنات من أي نوع.
بدءًا من v0.7.0 ، يوصى باستخدامه مع rxui.ViewModel . على عكس استخدام Decorator 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_agekey المعلمة الثاني: من أجل تتبع هوية كل عقدة ، وبالتالي إعادة استخدام العناصر الموجودة وإعادة ترتيبها ، يمكنك توفير مفتاح فريد للكتلة المقابلة لكل عنصر. يتم استخدام فهرس عنصر القائمة افتراضيًا. في المثال ، يُفترض أن اسم الجميع فريد من نوعه.store.get_item . نظرًا لأن الشخص نفسه يرث من rxui.ViewModel ، يمكن أن تكون خصائصه المختلفة مرتبطة مباشرة بالمكون. توفر جميع فئات المكونات bind_classes class الربط ، ودعم ثلاث هياكل بيانات مختلفة.
ربط القاموس
bg_color = to_ref ( False )
has_error = to_ref ( False )
rxui . label ( "test" ). bind_classes ({ "bg-blue" : bg_color , "text-red" : has_error })
rxui . switch ( "bg_color" , value = bg_color )
rxui . switch ( "has_error" , value = has_error ) قيمة مفتاح القاموس هي اسم الفئة والقيمة المقابلة هي متغير مستجيب مع Bool. عندما تكون القيمة المستجيبة True ، يتم تطبيق اسم الفصل على فئة المكون
ربط المتغيرات المستجيبة مع قيمة الإرجاع كقاموس
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 Function Parameter event_name query لعرض وثيقة Echarts الصينية
يربط المثال التالي حدث النقر بالماوس
from nicegui import ui
from ex4nicegui . reactive import rxui
opts = {
"xAxis" : { "type" : "value" , "boundaryGap" : [ 0 , 0.01 ]},
"yAxis" : {
"type" : "category" ,
"data" : [ "Brazil" , "Indonesia" , "USA" , "India" , "China" , "World" ],
},
"series" : [
{
"name" : "first" ,
"type" : "bar" ,
"data" : [ 18203 , 23489 , 29034 , 104970 , 131744 , 630230 ],
},
{
"name" : "second" ,
"type" : "bar" ,
"data" : [ 19325 , 23438 , 31000 , 121594 , 134141 , 681807 ],
},
],
}
bar = rxui . echarts ( opts )
def on_click ( e : rxui . echarts . EChartsMouseEventArguments ):
ui . notify ( f"on_click: { e . seriesName } : { e . name } : { e . value } " )
bar . on ( "click" , on_click )يثير المثال التالي حدث انتقاد الماوس فقط للسلسلة المحددة
from nicegui import ui
from ex4nicegui . reactive import rxui
opts = {
"xAxis" : { "type" : "value" , "boundaryGap" : [ 0 , 0.01 ]},
"yAxis" : {
"type" : "category" ,
"data" : [ "Brazil" , "Indonesia" , "USA" , "India" , "China" , "World" ],
},
"series" : [
{
"name" : "first" ,
"type" : "bar" ,
"data" : [ 18203 , 23489 , 29034 , 104970 , 131744 , 630230 ],
},
{
"name" : "second" ,
"type" : "bar" ,
"data" : [ 19325 , 23438 , 31000 , 121594 , 134141 , 681807 ],
},
],
}
bar = rxui . echarts ( opts )
def on_first_series_mouseover ( e : rxui . echarts . EChartsMouseEventArguments ):
ui . notify ( f"on_first_series_mouseover: { e . seriesName } : { e . name } : { e . value } " )
bar . on ( "mouseover" , on_first_series_mouseover , query = { "seriesName" : "first" })
ui . run ()قم بإنشاء Echart من رمز JavaScript
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 في الوظيفة. تحتوي الوظيفة أيضًا على معلمة ثانية ، وهي كائن عالمي 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 هي رابط شبكة بيانات خريطة صالحة. إذا كانت بيانات 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" )بعد تحميل الصفحة ، يتم عرض "Hello from T1" على الفور. عند التبديل إلى علامة التبويب "T2" ، سيتم عرض "Hello from T2".
تتيح لك طريقة 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" )قم بإنشاء تقارير مرئية للبيانات التفاعلية مع واجهات برمجة التطبيقات الأكثر تبسيطًا

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) ، هناك طريقتان لإنشاء مصادر البيانات.
DataFrame التي تتلقى pandas :
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 تحدد ما إذا كان سيتم استبعاد القيم الفارغة:
df = pd . DataFrame (
{
"cls" : [ "c1" , "c2" , "c1" , "c1" , "c3" , None ],
}
)
ds = bi . data_source ( df )
ds . ui_select ( "cls" , exclude_null_value = True )يمكنك تعيين معلمات مكون NativeGui المحدد من خلال معلمات الكلمات الرئيسية.
اضبط القيمة الافتراضية من خلال سمة القيمة:
ds . ui_select ( "cls" , value = [ 'c1' , 'c2' ])
ds . ui_select ( "cls" , multiple = False , value = 'c1' ) عندما يكون تحديد متعددة (المعلمة multiple صحيحًا بشكل افتراضي) ، يجب تحديد 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 . يتوافق field قيمة المفتاح في columnDefs مع اسم عمود مصدر البيانات. إذا لم يكن موجودًا ، فلن يسري التكوين.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 == "桌面" }
)
توليد رمز الاستجابة السريعة
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" )