Avaiga / taipy

Turns Data and AI algorithms into production-ready web applications in no time.
https://www.taipy.io
Apache License 2.0
14.32k stars 1.72k forks source link

[🐛 BUG] Table cell style not applied when columns refreshed with rebuild=True #2005

Open arcanaxion opened 1 week ago

arcanaxion commented 1 week ago

What went wrong? 🤔

Code example:

from taipy.gui import Gui
import taipy.gui.builder as tgb
import pandas as pd
import numpy as np

X_KEY = "x"
Y_KEY = "y"

def get_df():
    df = pd.DataFrame({X_KEY: range(100)})
    df[Y_KEY] = np.sin(df[X_KEY])
    return df

empty_table = pd.DataFrame(None)
df = empty_table

def cell_style(state, value, index, row, column_name):
    if value >= 0:
        return "bg-secondary"
    else:
        return ""

def row_style(state, index, row):
    if Y_KEY in row and row[Y_KEY] >= 0:
        return "bg-secondary"
    else:
        return ""

with tgb.Page() as page_tgb:
    tgb.button(label="Toggle df", on_action=lambda s: s.assign("df", get_df() if s.df.equals(empty_table) else empty_table))
    with tgb.layout(columns="1 1 1"):
        with tgb.part():
            tgb.text("Does NOT work: cell_class_name")
            tgb.table("{df}", rebuild=True, page_size=10, **{f"cell_class_name__{Y_KEY}": cell_style})
        with tgb.part():
            tgb.text("Works: row_class_name")
            tgb.table("{df}", rebuild=True, page_size=10, row_class_name=row_style)
        with tgb.part():
            tgb.text("Works: cell_class_name, but df isn't 'rebuilt'")
            tgb.table("{get_df()}", rebuild=True, page_size=10, **{f"cell_class_name__{Y_KEY}": cell_style})

def on_change(state, var_name: str, var_value):
    print(var_name, type(var_value), var_value)

if __name__ == "__main__":
    gui = Gui(page_tgb)
    gui.run(debug=True, run_browser=False, use_reloader=True)

Before clicking button: image

After clicking button: image

Expected Behavior

The cell style should be applied when columns are refreshed when rebuild=True.

Steps to Reproduce Issue

Run example code above

Browsers

Chrome

OS

Windows, Linux

Version of Taipy

4.0.0

Acceptance Criteria

Code of Conduct

FlorianJacta commented 1 week ago

I was able to reproduce it and if you refresh the page, you will then see the styling/color on the cells.

the-raja commented 4 days ago

The issue seems to arise from how the Taipy GUI framework handles the cell_class_name attribute and the data table's rebuilding process.

  1. When using rebuild=True, the state might not correctly refresh or reapply the cell-level styles using cell_class_name. This could be due to a mismatch between how Taipy handles data updates internally.

  2. While row_class_name works fine, cell_class_name might not trigger consistently with dynamic changes because the rebuilding logic needs to detect which cells have changed.

  3. When weswitch between the empty table and the generated DataFrame, the state might not properly register the update, orcell_class_name might not be reapplied after df is toggled.

Fix :

  1. Try forcing the table to rebuild after toggling df
    tgb.button(
    label="Toggle df",
    on_action=lambda s: (s.assign("df", get_df() if s.df.equals(empty_table) else empty_table), s.run_method("rebuild"))
    )
  2. Use state.df Explicitly in cell_style
    
    def cell_style(state, value, index, row, column_name):
    if column_name == Y_KEY and value >= 0:
        return "bg-secondary"
    return ""
3. Debug with Logs

def cell_style(state, value, index, row, column_name): print(f"Cell Style Applied: {value} at {index}") return "bg-secondary" if value >= 0 else ""


UPDATED CODE:

from taipy.gui import Gui import taipy.gui.builder as tgb import pandas as pd import numpy as np

X_KEY = "x" Y_KEY = "y"

def get_df(): df = pd.DataFrame({X_KEY: range(100)}) df[Y_KEY] = np.sin(df[X_KEY]) return df

empty_table = pd.DataFrame(None) df = empty_table

def cell_style(state, value, index, row, column_name): print(f"Cell Style Applied: {value} at {index}") return "bg-secondary" if value >= 0 else ""

def row_style(state, index, row): if Y_KEY in row and row[Y_KEY] >= 0: return "bg-secondary" return ""

with tgb.Page() as page_tgb: tgb.button( label="Toggle df", on_action=lambda s: ( s.assign("df", get_df() if s.df.equals(empty_table) else empty_table), s.run_method("rebuild") ) ) with tgb.layout(columns="1 1 1"): with tgb.part(): tgb.text("Does NOT work: cell_class_name") tgb.table("{df}", rebuild=True, page_size=10, {f"cell_class_name__{Y_KEY}": cell_style}) with tgb.part(): tgb.text("Works: row_class_name") tgb.table("{df}", rebuild=True, page_size=10, row_class_name=row_style) with tgb.part(): tgb.text("Works: cell_class_name, but df isn't 'rebuilt'") tgb.table("{get_df()}", rebuild=True, page_size=10, {f"cell_class_name__{Y_KEY}": cell_style})

def on_change(state, var_name: str, var_value): print(var_name, type(var_value), var_value)

if name == "main": gui = Gui(page_tgb) gui.run(debug=True, run_browser=False, use_reloader=True)