Skip to content

LineCharts with custom bottom_axis labels stop rendering after list max length is reached #16

@mgeirin

Description

@mgeirin

Duplicate Check

Describe the bug

List for bottom_axis.labels is passed through a deque of certain maxlen (in this example, 20). Charts start rendering as expected, but when length of deque reaches maxlen, charts stop rendering. If bottom_axis.labels is not modified, charts do not freeze. List for bottom_axis.labels and rest of data_series keep being produced as expected. Also, if chart.min_x is modified, lines start rendering outside of plot area.

Code sample

Code
import flet as ft
import random
from collections import deque
from time import localtime, strftime, perf_counter_ns, sleep

def main(page: ft.Page):
    page.title = "Chart bug demo"
    page.vertical_alignment = ft.MainAxisAlignment.CENTER

    def temp_chart(name: str, max_y: float, min_y: float) -> ft.Card:
        return ft.Card(
            content=ft.LineChart(
                border=ft.border.all(3, ft.colors.with_opacity(0.4, ft.colors.ON_SURFACE)),
                horizontal_grid_lines=ft.ChartGridLines(interval=1, color=ft.colors.with_opacity(0.2, ft.colors.ON_SURFACE), width=1),
                vertical_grid_lines=ft.ChartGridLines(interval=6, color=ft.colors.with_opacity(0.2, ft.colors.ON_SURFACE), width=1),
                expand=True,
                tooltip_bgcolor=ft.colors.with_opacity(0.8, ft.colors.GREY_200),
                left_axis=ft.ChartAxis(labels_size=40, title=ft.Text(value="Temperatura (°C)", size=15), title_size=30),
                bottom_axis=ft.ChartAxis(
                    labels_size=20,
                    show_labels=True,
                    title=ft.Text(value="Hora", size=15),
                    title_size=30
                ),
                top_axis=ft.ChartAxis(title=ft.Text(value=name, size=20), title_size=40, show_labels=False),
                right_axis=ft.ChartAxis(title=ft.Text(value=""), title_size=20, show_labels=False),
                height=400,
                width=600,
                max_y=max_y,
                min_y=min_y
            )
        )

    d_size = 144

    pt_dict = {
        "H.LCS.025": {
            "name": "H.LCS.025",
            "max_y": 8.0,
            "min_y": 0.0,
            "upper_limit_dp": deque(maxlen=d_size),
            "upper_limit_val": 6.0,
            "upper_limit_color": ft.colors.BLACK,
            "left_sensor_dp": deque(maxlen=d_size),
            "left_sensor_idx": 0,
            "left_sensor_color": ft.colors.GREEN,
            "right_sensor_dp": deque(maxlen=d_size),
            "right_sensor_idx": 1,
            "right_sensor_color": ft.colors.LIGHT_GREEN,
            "lower_limit_dp": deque(maxlen=d_size),
            "lower_limit_val": 2.0,
            "lower_limit_color": ft.colors.BLACK
        },
        "H.LCS.027": {
            "name": "H.LCS.027",
            "max_y": 10.0,
            "min_y": 0.0,
            "upper_limit_dp": deque(maxlen=d_size),
            "upper_limit_val": 8.0,
            "upper_limit_color": ft.colors.BLACK,
            "left_sensor_dp": deque(maxlen=d_size),
            "left_sensor_idx": 2,
            "left_sensor_color": ft.colors.GREEN,
            "right_sensor_dp": deque(maxlen=d_size),
            "right_sensor_idx": 3,
            "right_sensor_color": ft.colors.LIGHT_GREEN,
            "lower_limit_dp": deque(maxlen=d_size),
            "lower_limit_val": 4.0,
            "lower_limit_color": ft.colors.BLACK
        },
        "H.LCS.024": {
            "name": "H.LCS.024",
            "max_y": 8.0,
            "min_y": 0.0,
            "upper_limit_dp": deque(maxlen=d_size),
            "upper_limit_val": 6.0,
            "upper_limit_color": ft.colors.BLACK,
            "left_sensor_dp": deque(maxlen=d_size),
            "left_sensor_idx": 4,
            "left_sensor_color": ft.colors.GREEN,
            "right_sensor_dp": deque(maxlen=d_size),
            "right_sensor_idx": 5,
            "right_sensor_color": ft.colors.LIGHT_GREEN,
            "lower_limit_dp": deque(maxlen=d_size),
            "lower_limit_val": 2.0,
            "lower_limit_color": ft.colors.BLACK
        },
        "H.LCS.026": {
            "name": "H.LCS.026",
            "max_y": 8.0,
            "min_y": 0.0,
            "upper_limit_dp": deque(maxlen=d_size),
            "upper_limit_val": 6.0,
            "upper_limit_color": ft.colors.BLACK,
            "left_sensor_dp": deque(maxlen=d_size),
            "left_sensor_idx": 6,
            "left_sensor_color": ft.colors.GREEN,
            "right_sensor_dp": deque(maxlen=d_size),
            "right_sensor_idx": 7,
            "right_sensor_color": ft.colors.LIGHT_GREEN,
            "lower_limit_dp": deque(maxlen=d_size),
            "lower_limit_val": 2.0,
            "lower_limit_color": ft.colors.BLACK
        },
        "F.LCS.002/003": {
            "name": "F.LCS.002/003",
            "max_y": 8.0,
            "min_y": 0.0,
            "upper_limit_dp": deque(maxlen=d_size),
            "upper_limit_val": 6.0,
            "upper_limit_color": ft.colors.BLACK,
            "left_sensor_dp": deque(maxlen=d_size),
            "left_sensor_idx": 8,
            "left_sensor_color": ft.colors.BLUE,
            "right_sensor_dp": deque(maxlen=d_size),
            "right_sensor_idx": 9,
            "right_sensor_color": ft.colors.LIGHT_BLUE,
            "lower_limit_dp": deque(maxlen=d_size),
            "lower_limit_val": 2.0,
            "lower_limit_color": ft.colors.BLACK
        }
    }

    temp_charts = [
        temp_chart(
            name=pt_dict[key]["name"],
            max_y=pt_dict[key]["max_y"],
            min_y=pt_dict[key]["min_y"]
        )
        for key in pt_dict
    ]

    page.add(
        ft.Row(
            [
                temp_charts[0],
                temp_charts[1],
                temp_charts[2]
            ],
            alignment=ft.MainAxisAlignment.CENTER,
        ),
        ft.Row(
            [
                temp_charts[3],
                temp_charts[4]
            ],
            alignment=ft.MainAxisAlignment.CENTER,
        )
    )

    def gen_yaxis_data():
        return [random.random() * 4 + 2 for _ in range(10)]

    def build_data_series(data_points: deque, tracker: float, y: float, color: ft.colors) -> ft.LineChartData:
        data_points.append(ft.LineChartDataPoint(tracker, y))
        data_series = ft.LineChartData(
            data_points=list(data_points),
            stroke_width=5,
            color=color,
            curved=True,
            stroke_cap_round=True,
            prevent_curve_over_shooting=True
        )
        return data_series

    def build_time_labels(time_labels: deque, tracker: float) -> deque([ft.ChartAxisLabel]):
        time_labels.append(ft.ChartAxisLabel(label=ft.Text(value=strftime("%H", localtime()), size=10), value=tracker))
        return time_labels

    def update_chart(data_series: [ft.LineChartData], time_labels: [ft.ChartAxisLabel], chart: ft.LineChart) -> None:
        chart.data_series = data_series
        chart.bottom_axis.labels = time_labels
        #chart.min_x = time_labels[0].value
        #chart.max_x = time_labels[-1].value

    def pt_main(page: ft.Page, pt_dict: {}, temp_charts: [ft.LineChart]) -> None:
        time_labels = deque(maxlen=20)
        tracker = 0.0
        while True:
            t0 = perf_counter_ns()
            temps = gen_yaxis_data()
            time_labels = build_time_labels(time_labels, tracker)
            for _, chart in zip(pt_dict, temp_charts):
                upper_limit = build_data_series(
                    pt_dict[_]["upper_limit_dp"],
                    tracker,
                    pt_dict[_]["upper_limit_val"],
                    pt_dict[_]["upper_limit_color"]
                )
                left_sensor = build_data_series(
                    pt_dict[_]["left_sensor_dp"],
                    tracker,
                    temps[pt_dict[_]["left_sensor_idx"]],
                    pt_dict[_]["left_sensor_color"]
                )
                right_sensor = build_data_series(
                    pt_dict[_]["right_sensor_dp"],
                    tracker,
                    temps[pt_dict[_]["right_sensor_idx"]],
                    pt_dict[_]["right_sensor_color"]
                )
                lower_limit = build_data_series(
                    pt_dict[_]["lower_limit_dp"],
                    tracker,
                    pt_dict[_]["lower_limit_val"],
                    pt_dict[_]["lower_limit_color"]
                )
                data_series = [
                    upper_limit,
                    left_sensor,
                    right_sensor,
                    lower_limit
                ]
                update_chart(data_series, list(time_labels), chart.content)
            page.update()
            tracker += 1
            t1 = perf_counter_ns()
            sleep(1 - (t1 - t0) / 1_000_000_000)

    pt_main(page, pt_dict, temp_charts)

ft.app(main)

To reproduce

Run repro code and wait for maxlen to reach 20 (20 seconds), modify maxlen accordingly to see problem sooner.

Expected behavior

Expected to see charts not freezing and rendering continuously.

Screenshots / Videos

Captures

[Upload media here]

Operating System

Windows

Operating system details

Windows 11

Flet version

0.23.2

Regression

I'm not sure / I don't know

Suggestions

No response

Logs

Logs
[Paste your logs here]

Additional details

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions