Package: ex4nicegui
ex4nicegui中文 READMEInstallUsageFeaturesBI ModuleAn extension library fornicegui. It has built-in responsive components and fully implements data-responsive interface programming.📦 Installpip install ex4nicegui -Uexamplesbasictodo list mvc🦄 Usagefromniceguiimportuifromex4niceguiimportref_computed,effect,to_reffromex4nicegui.reactiveimportrxui# Define responsive datar_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()🚀 Featuresecharts componentsfromniceguiimportuifromex4niceguiimportref_computed,effect,to_reffromex4nicegui.reactiveimportrxuir_input=to_ref("")# ref_computed Creates a read-only reactive variable# Functions can use any other reactive variables automatically when they are associated@ref_computeddefcp_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 objectinput.element.classes("w-full")rxui.echarts(cp_echarts_opts).classes('h-[20rem]')ui.run()echarts mouse eventstheonfunction parametersevent_nameandqueryto view theecharts English DocumentationThe following example binds the mouse click eventfromniceguiimportuifromex4nicegui.reactiveimportrxuiopts={"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)defon_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.fromniceguiimportuifromex4nicegui.reactiveimportrxuiopts={"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)defon_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()responsivefromex4niceguiimport(to_ref,ref_computed,on,effect,effect_refreshable,batch,event_batch,deep_ref,)Commonly usedto_ref,deep_ref,effect,ref_computed,onto_refDefines responsive objects, read and written by.value.a=to_ref(1)b=to_ref("text")a.value=2b.value='new text'print(a.value)When the value is a complex object, responsiveness of nested objects is not maintained by default.a=to_ref([1,2])@effectdef_().print(len(a.value))# doesn't trigger the effecta.value.append(10)# the whole substitution will be triggereda.value=[1,2,10]Depth responsiveness is obtained whenis_deepis set toTrue.a=to_ref([1,2],is_deep=True)@effectdef_():print('len:',len(a.value))# print 3a.value.append(10)deep_refis equivalent toto_refifis_deepis set toTrue.deep_refEquivalent toto_refwhenis_deepis set toTrue.Especially useful when the data source is a list, dictionary or custom class. Objects obtained via.valueare proxiesdata=[1,2,3]data_ref=deep_ref(data)assertdata_ref.valueisnotdataYou can get the raw object withto_raw.fromex4niceguiimportto_raw,deep_refdata=[1,2,3]data_ref=deep_ref(data)assertdata_ref.valueisnotdataassertto_raw(data_ref.value)isdataeffectAccepts a function and automatically monitors changes to the responsive objects used in the function to automatically execute the function.a=to_ref(1)b=to_ref("text")@effectdefauto_run_when_ref_value():print(f"a:{a.value}")defchange_value():a.value=2b.value="new text"ui.button("change",on_click=change_value)The first time the effect is executed, the functionauto_run_when_ref_valuewill be executed once. After that, clicking on the button changes the value ofa(viaa.value) and the functionauto_run_when_ref_valueis executed again.Never spread a lot of data processing logic across multipleons oreffectss, which should be mostly interface manipulation logic rather than responsive data processing logicref_computedAs witheffect,ref_computedcan also return results from functions. Typically used for secondary computation fromto_ref.a=to_ref(1)a_square=ref_computed(lambda:a.value*2)@effectdefeffect1():print(f"a_square:{a_square.value}")defchange_value():a.value=2ui.button("change",on_click=change_value)When the button is clicked, the value ofa.valueis modified, triggering a recalculation ofa_square. As the value ofa_squareis read ineffect1, it triggerseffect1to execute theref_computedis read-onlyto_refIf you prefer to organize your code by class,ref_computedalso supports acting on instance methodsclassMyState.def__init__(self)->None.self.r_text=to_ref("")@ref_computeddefpost_text(self):returnself.r_text.valuereturnself.r_text.value+"post"state=MyState()rxui.input(value=state.r_text)rxui.label(state.post_text)onSimilar toeffect, butonneeds to explicitly specify the responsive object to monitor.a1=to_ref(1)a2=to_ref(10)b=to_ref("text")@on(a1)defwatch_a1_only():print(f"watch_a1_only ... a1:{a1.value},a2:{a2.value}")@on([a1,b],onchanges=True)defwatch_a1_and_b():print(f"watch_a1_and_b ... a1:{a1.value},a2:{a2.value},b:{b.value}")defchange_a1():a1.value+=1ui.notify("change_a1")ui.button("change a1",on_click=change_a1)defchange_a2():a2.value+=1ui.notify("change_a2")ui.button("change a2",on_click=change_a2)defchange_b():b.value+="x"ui.notify("change_b")ui.button("change b",on_click=change_b)If the parameteronchangesis 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 multipleons oreffectss, which should be mostly interface manipulation logic rather than responsive data processing logicvforRender list components based on list responsive data. Each component is updated on demand. Data items support dictionaries or objects of any typefromniceguiimportuifromex4nicegui.reactiveimportrxuifromex4niceguiimportto_ref,ref_computed# refsitems=to_ref([{"id":1,"message":"foo","done":False},{"id":2,"message":"bar","done":True},])# ref_computeds@ref_computeddefdone_count_info():returnf"done count:{sum(item['done']foriteminitems.value)}"# methoddefcheck():foriteminitems.value:item["done"]=notitem["done"]items.value=items.value# uirxui.label(done_count_info)ui.button("check",on_click=check)@rxui.vfor(items,key='id')def_(store:rxui.VforStore):# function to build the interface for each row of datamsg_ref=store.get("message")# Get responsive object with `store.get`# Enter the content of the input box,# you can see the title of the radio box changes synchronouslywithui.card():rxui.input(value=msg_ref)rxui.checkbox(text=msg_ref,value=store.get("done"))rxui.vfordecorator to custom functionThe first argument is passed to the responsive list. Each item in the list can be a dictionary or other object (dataclassesetc.)Second parameterkey: 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 attribute can be retrieved viastore.get, which is a responsive object.vfor are created only when new data is added.In the above example, you'll notice that when the checkbox is clicked, the text of the number of completed counts (done_count_info) doesn't change synchronouslyThis is because responsive data changes in thevforfunction do not affect the data source list. This is a restriction to prevent writing overly complex bi-directional data flow response logic.We should make changes to the data source list via events in the function...@rxui.vfor(items,key="id")def_(store:rxui.VforStore):msg_ref=store.get("message")defon_check_change(e):items.value[store.row_index]["done"]=e.valueitems.value=items.valuewithui.card():rxui.input(value=msg_ref)rxui.checkbox(text=msg_ref,value=store.get("done"),on_change=on_check_change)functionalityBind class namesAll component classes providebind_classesfor bindingclass, supporting three different data structures.Bind dictionariesbg_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 valuebool. When the responsive value isTrue, the class name is applied to the componentclass.Bind a responsive variable whose return value is a dictionary.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 passingrxui.label("bind to ref_computed").bind_classes(lambda:{"bg-blue":bg_color.value,"text-red":has_error.value})Bind to listbg_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 namebind-stylefromniceguiimportuifromex4nicegui.reactiveimportrxuifromex4nicegui.utils.signalsimportto_refbg_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_stylepassed into dictionary,keyis style name,valueis style value, responsive stringrxui.echartsCharting with echartsrxui.echarts.from_javascriptCreate echart from javascript codefrompathlibimportPathrxui.echarts.from_javascript(Path("code.js"))# orrxui.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 withsetOption.rxui.echarts.register_mapRegister a map.rxui.echarts.register_map("china","https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json")rxui.echarts({"geo":{"map":"china","roam":True,},"tooltip":{},"legend":{},"series":[],}})The parametermap_nameis a customized map name. Note thatmapmust correspond to a registered name in the chart configuration.The parametersrcis a valid network link to the map data.You can also provide the path to the local json file for the map data.frompathlibimportPathrxui.echarts.register_map("china",Path("map-data.json"))gsapjs animation library.gsap documentationfromniceguiimportuifromex4niceguiimportgsapgsap.from_Set the start property, the animation will transition from the set property to the original positionui.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.Argumentstargetsare css selectors.Argumentsvarsare attribute valuesgsap.toSet the end property, the animation will transition from the original property to the set property.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.Argumentstargetsare css selectors.Argumentsvarsare attribute valuesgsap.run_scriptSetting up animations by writing jsgsap.run_script(r"""function setGsap(gsap) {gsap.to('.target',{"duration": 0.3,y:60})}""")The parameterscriptcan be text or a file with a js extensionPath.The name of the defined js function doesn't matter, the first argument is a gsap object.BI ModuleCreate an interactive data visualization report using the minimal API.fromniceguiimportuiimportpandasaspdimportnumpyasnpfromex4niceguiimportbifromex4nicegui.reactiveimportrxuifromex4niceguiimporteffect,effect_refreshablefrompyecharts.chartsimportBar# data readydefgen_data():np.random.seed(265)field1=["a1","a2","a3","a4"]field2=[f"name{i}"foriinrange(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))returndfdf=gen_data()# Create a data source.ds=bi.data_source(df)# uiui.query(".nicegui-content").classes("items-stretch no-wrap")withui.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]")withui.grid(columns=2):# Configure the chart using a dictionary.@ds.ui_echartsdefbar1(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_echartsdefbar2(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_clickdef_(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.@effectdef_():# prop `filtered_data` is the filtered DataFrame.df=ds.filtered_datatotal=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_refreshabledef_():df=ds.filtered_datatotal=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}")defdetails_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_sourcedefnew_ds():returnds.filtered_data[["name","idc1","idc2"]]new_ds.ui_aggrid()ui.run()Detailsbi.data_sourceThe 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.Receivepandas'sDataFrame:fromniceguiimportuifromex4niceguiimportbiimportpandasaspddf=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:df=pd.DataFrame({"name":list("aabcdf"),"cls":["c1","c2","c1","c1","c3",None],"value":range(6),})ds=bi.data_source(df)@bi.data_sourcedefnew_ds():# df is pd.DataFramedf=ds.filtered_datadf=df.copy()df['value']=df['value']*100returndfds.ui_select('name')new_ds.ui_aggrid()Note that sincenew_dsusesds.filtered_data, changes todswill trigger the linkage change ofnew_ds, causing the table component created bynew_dsto change.Remove all filter states through theds.remove_filtersmethod:ds=bi.data_source(df)defon_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 theds.reloadmethod: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)defon_remove_filters():ds.reload(new_df)ui.button("reload data",on_click=on_remove_filters)ds.ui_select("name")ds.ui_aggrid()ui_selectDropdown Select Boxfromniceguiimportuifromex4niceguiimportbiimportpandasaspddf=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 parametersort_options:ds.ui_select("name",sort_options={"value":"desc","name":"asc"})Set whether to exclude null values using the parameterexclude_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)You can set the parameters of the native nicegui select component through keyword arguments.Set default values through the value attribute:ds.ui_select("cls",value=['c1','c2'])ds.ui_select("cls",multiple=False,value='c1')For multiple selections (the parametermultipleis defaulted to True),valueneeds to be specified as a list. For single selections,valueshould be set to non-list.ui_tableTablefromniceguiimportuifromex4niceguiimportbiimportpandasaspddata=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 parametercolumnsare consistent with niceguiui.table. The key valuefieldcorresponds to the column name of the data source, and if it does not exist, this configuration will not take effectTherowsparameter will not take effect. Because the data source of the table is always controlled by the data source.ui_aggridfromniceguiimportuifromex4niceguiimportbiimportpandasaspddata=pd.DataFrame({"colA":list("abcde"),"colB":[f"n{idx}"foridxinrange(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 parameteroptionsis consistent with niceguiui.aggrid. The key valuefieldin columnDefs corresponds to the column name of the data source, and if it does not exist, this configuration will not take effect.TherowDatakey value will not take effect. Because the data source of the table is always controlled by the data source.