Search code examples
pythonpandasflet

Flet DataTable doesn't update when conditions are met


I'm making a reporting app with Flet and i need to have a dropdown with values from which a user will pick one. After picking the value, a table besides the dropdown will need to be updated with the calculated data based on the picked value. This type of procedure will be a recurring element of the whole app.

In this stage, it seems that the data is calculated correctly, but flet.DataCell does some weird thing and the data is lost in the process. Below is the sample code:

class AppFace:
  def __init__(self, page):
        self.page = page
        self.page.title = "KPI Reporting v.01"
        self.page.horizontal_alignment = ft.CrossAxisAlignment.CENTER

        # Add the filepicker to the page
        self.file_picker = ft.FilePicker(on_result=self.on_file_selected)
        self.page.overlay.append(self.file_picker)

        self.file_output = ft.Text(value="No File Selected")

        # Initialize sidebar and content area
        self.sidebar = self.create_sidebar()
        self.content = ft.Column(controls=[ft.Text("Welcome to the app!")], expand=True)

        # Home Page default elements
        self.raw_drop = consolidate.GatherData().form_combo()

        self.dropdown_var = None

        self.dropdown = ft.Dropdown(
            hint_text="Choose a department",
            width=200,
            options=[ft.dropdown.Option(value) for value in self.raw_drop],
            on_change=self.drop_changed,
        )

        self.empty_text_label = ft.Text()

        self.update_button = ft.FilledButton(
            text="Update Table", on_click=self.update_table
        )

        # Layout with sidebar and main content
        self.page.add(
            ft.Row(
                controls=[
                    ft.Container(content=self.sidebar, width=150),
                    ft.VerticalDivider(width=1),
                    self.content,
                ],
                expand=True,
            )
        )
        self.show_home()

    def drop_changed(self, e):
        self.dropdown_var = e.control.value
        self.empty_text_label.value = f"Selected Value: {self.dropdown_var}"
        self.show_home()

    def update_table(self):
        df = file_ops.FilePrep().split_by_snap()[1]
        df_filtered = (
            df[df["department"] == self.dropdown_var] if self.dropdown_var else df
        )

        df_to_convert = kpi.EmployeeAnalytics(
            df_filtered, self.empty_text_label
        ).form_df()


        datatable = ft.DataTable()

        for col in df_to_convert.columns:
            datatable.columns.append(ft.DataColumn(ft.Text(col)))

        for i in range(len(df_to_convert)):
            datatable.rows.append(
                ft.DataRow(
                    cells=[
                        ft.DataCell(ft.Text(str(df_to_convert.iloc[i, j])))
                        for j in range(len(df_to_convert.columns))
                    ]
                )
            )

        if initialize:
            self.content.controls = [
                ft.Text("Welcome to the KPI Reporting App"),
                ft.Container(
                    content=ft.Column(
                        [
                            ft.Text("Combobox section"),
                            self.dropdown,
                            self.update_button,
                            self.empty_text_label,
                        ]
                    ),
                ),
                ft.Container(
                    content=datatable,
                ),
            ]
        else:
            updated = False
            for index, control in enumerate(self.content.controls):
                if isinstance(control, ft.Container) and isinstance(
                    control.content, ft.DataTable
                ):
                    self.content.controls[index] = ft.Container(
                        content=datatable,
                    )
                    updated = True
                    break

            if not updated:
                self.content.controls.append(
                    ft.Container(
                        content=datatable,
                    )
                )

        self.page.update()

      def show_home(self):
        self.empty_text_label = ft.Text()
        self.update_table(initialize=True)
        self.page.update()

Additional details:

  • consolidate.create_flet_table(param) is a method that extracts the data from a calculated dataframe and stores the columns and the data in a tuple
  • file_ops.FilePrep().split_by_snap()[1] is a part of a dataframe
  • kpi.EmployeeAnalytics(*args).form_df() is a dataframe made from compiling data resulted from calculating data from the above dataframe
  • consolidate.GatherData().form_combo() is a list of uniwue values. The values of the dropdown.

The added button can be deleted of the table updates after the dropdown value is picked.


Solution

  • I've solved the issue with these lines and changes, using the guindance from this answer:

    Basically, the label containing the department wasn't passed correctly into the update function and the table wouldn't display according to the new value selected.

    Note: I've removed aesthetic elements from the answer to de-clutter the relevant block of code, and the button from __init__ because now, when the dropdown changes, so does the table automatically.

    Changes in the app class:

        def drop_changed(self, e):
            self.dropdown_var = e.control.value
            self.empty_text_label.value = f"Selected Value: {self.dropdown_var}"
    
            self.content.controls[1].controls[1].content = self.update_table(
                self.dropdown_var
            )
    
            self.page.update()
    
        def update_table(self, cval):
            df = file_ops.FilePrep().split_by_snap()[1]
    
            df_to_convert = kpi.EmployeeAnalytics(df, cval).form_df()
    
            datatable = ft.DataTable(
                columns=consolidate.headers(df_to_convert),
                rows=consolidate.rows(df_to_convert),
            )
    
            return datatable
    
       def show_home(self):
            table1 = self.update_table(self.dropdown_var)
    
            self.content.controls = [
                ft.Text("Welcome to the KPI Reporting App"),
                ft.Row(
                    [
                        ft.Container(
                            content=ft.Column(
                                [
                                    ft.Text("Combobox section"),
                                    self.dropdown,
                                    self.empty_text_label,
                                ]
                            ),
                        ),
                        ft.Container(
                            content=ft.Row(
                                [
                                    table1,
                                ]
                            ),
                        ),
                    ]
                ),
            ]
    
            self.page.update()
    

    Changes in the consolidate package

    def rows(df: pd.DataFrame) -> list:
        rows = []
        for _, row in df.iterrows():
            rows.append(
                ft.DataRow(
                    cells=[ft.DataCell(ft.Text(row[header])) for header in df.columns]
                )
            )
        return rows
    
    
    def headers(df: pd.DataFrame) -> list:
        return [ft.DataColumn(ft.Text(header)) for header in df.columns]