Avaiga/taipy-gui

BUG-Charts briefly resets to empty when refreshed

gbritoda opened this issue · 3 comments

Description
Whenever a chart is refreshed, it resets quickly to an empty chart before updating. While this is not very noticeable in faster systems, if there's an slower connection to the GUI or the system is running slow due to handling a lot of data or something, it gets more noticeable.
It also gets very noticeable if you have fast updating data, like a live graph tracking something, which is my example below:

How to reproduce
In this code, we should have a continuous sine wave on the graph updating every 0.1 second when the button is pressed.
But since the graphs blinks every update (the need to refresh is due to Avaiga/taipy#466), it's very hard to read the labels

from taipy.gui import Gui
from time import sleep
from math import sin

data = {
        "x":[],
        "y":[]
    }

page = """
<|button|label=start|on_action=start_button_pressed|>
<|{data}|chart|x=x|y=y|rebuild=True|propagate|>
"""

def start_button_pressed(state):
    global data
    x = 0
    while True:
        data['x'].append(x)
        data['y'].append(sin(x))
        state.refresh("data")
        x+=1
        sleep(0.1)

gui = Gui(page=page)

gui.run(
    host="0.0.0.0",
    port=1234,
    debug=True,
    dark_mode=False,
    use_reloader=True
)

Expected behavior
A continuously flowing graph of a sine wave

Runtime environment
Please specify relevant indications.

  • OS: Ubuntu 22.04.2 LTS on WSL
  • Browser: Firefox and Chrome
  • Taipy 2.4.0

Could you help me understand the issue at hand? Is the behavior you describe only visible with many points, or is it only due to a change of data?

A part of your issue could be that Taipy will change its x-axis and y-axis depending on the data. You can make the chart axes static. However, I believe you are talking about labels of data points.

Here is an example of code with static axes. Would it help?

from taipy.gui import Gui
from time import sleep
from math import sin

data = {
        "x":[],
        "y":[]
    }

# my layout to have static axis
layout = {
        "xaxis": {
            "range": [0, 100],  
        },
        "yaxis": {
            "range": [-1, 1], 
        }
    }

page = """
<|button|label=start|on_action=start_button_pressed|>
<|{data}|chart|x=x|y=y|layout={layout}|>
"""

def start_button_pressed(state):
    x = 0
    while x < 100:
        data['x'].append(x)
        data['y'].append(sin(x))

        state.data = data # I am using the assignment directly and not the refresh function

        x+=1
        sleep(0.1)

gui = Gui(page=page)
gui.run()

On a side note: The rebuild property is not needed here; it is used when the data changes its structure (for example, when the column names change). Propagate property is also not required.

Got my names mixed! Meant axes not labels
So what happens is:

  • Graph has a point
  • Graph gets updated in the code to have another point
  • In the GUI, the Graph briefly shows up as empty before showing the next point.
  • For graphs that already populated with lots of data this is more noticeable because the axes change more. Also for slower systems

That's the output of my code:
graph 2
See the flickering?

Your solution makes it less noticeable because the axes don't change. But the graph still blinks everytime its data is refreshed.
Hoping it makes sense 😅

We are looking into this issue currently.

Also, on a side note, it is better to use the ìnvoke_long_callback` function for live data updates as such:

from taipy.gui import Gui, invoke_long_callback
from math import sin
import time

data = {
        "x":[],
        "y":[]
    }

# my layout to have static axis
layout = {
        "xaxis": {
            "range": [0, 100],  
        },
        "yaxis": {
            "range": [-1, 1], 
        }
    }

page = """
<|{data}|chart|x=x|y=y|layout={layout}|>
"""

x = 1

def iddle():
     while True:
          time.sleep(1)

def update(state):
    data = state.data
    data['x'].append(state.x)
    data['y'].append(sin(state.x))

    state.data = data # I am using the assignment directly and not the refresh function

    state.x+=1

def on_init(state):
    invoke_long_callback(state, iddle, [], update, [], 500)


gui = Gui(page=page)
gui.run()