Prevent plotly figures open new browser tabs when using add_traces( ) and show( )

I’m writting a python script that retrive EUR/USD data prices and display them with plotly on my web browser. Every minutes a new price is received, so I use the add_traces( ) function to update my figure and figure.show( ) to display the result.

But the show function open a new tab every time.

How can I use only 1 tab and update my data on this single tab ?

Hello @tessan,

Welcome to the community!

Every time you use figure.show(), it will automatically open the new figure into a new window.

You’d have to use a callback in order to update the figure’s information.

1 Like

Isn’t callback a Dash feature? I would prefer to not use Dash if possible because I can’t use Dash in multi-threading. Can you give me a sample code please ?

Here, take a look at this:

1 Like

It does not really help me.
Here is what I’ve tried so far with Dash framework :

class Chart_renderer:
    app = Dash('not main')
    app.layout = html.Div([
        html.H4('Apple stock candlestick chart'),
    
        dcc.Graph(id="graph"),

        dcc.Interval(
            id='interval-component',
            interval=2*1000, # in milliseconds
            n_intervals=0
        )
    ])
    
    df_fixed_length = 50 

    def __init__(self, df_data):

        self.df = df_data

        self.fig = go.Figure(
            go.Candlestick(
                x=self.df.index,
                open=self.df['bidopen'],
                high=self.df['bidhigh'],
                low=self.df['bidlow'],
                close=self.df['bidclose']
            )
        )

    def add_new_data(self, new_df_row):
        self.df = pd.concat([self.df, new_df_row], axis = 0)

    @app.callback(Output("graph", "figure"), Input('interval-component', 'n_intervals'))
    def display_candlestick(self, n_intervals):
        print('n_interval:', n_intervals)
        # if n_intervals == 0 we just display the current self.fig
        if n_intervals > 0:
            while True:
                if self.df_fixed_length < len(self.df):
                    self.fig.add_trace(
                        go.Candlestick(
                            x=[self.df.index[-1]],
                            open=[self.df['bidopen'].iloc[-1]],
                            high=[self.df['bidhigh'].iloc[-1]],
                            low=[self.df['bidlow'].iloc[-1]],
                            close=[self.df['bidclose'].iloc[-1]]
                    ))
                    self.df = self.df.drop(self.df.index[0], axis=0)
                    break
                time.sleep(5)

        return self.fig


ACCESS_TOKEN = 'xxxx'
conn = fxcmpy.fxcmpy(access_token=ACCESS_TOKEN, log_level='debug', server='demo', log_file='log.txt')
print("Connected to FXCM server!")
# request 50 candlestick prices :
df_prices = conn.get_candles('EUR/USD', period='m1', number=50)

renderer = Chart_renderer(df_prices)

app_thread = Thread(target=renderer.app.run_server, kwargs={'debug':True})
app_thread.start()

# renderer.fig.show()

while(True):
    nbr_positions = len(conn.get_open_positions())
    if nbr_positions == 0:
        current_time = dt.datetime.now()
        # Check for updated price data every minutes
        if current_time.minute % 1 == 0 :
            df_new_price_value = conn.get_candles('EUR/USD', period='m1', number=1)

            if(df_new_price_value.index[-1] != renderer.df.index[-1]):
                # if price is new we add the new data to the dataframe
                renderer.add_new_data(df_new_price_value)
                # df_new_price_value = df_new_price_value['bidclose']
                # fig = renderer.display_candlestick()
                # fig.show()
                print("step")
             
    else:
        print("trade in process ... ")

But I have this error :

Connected to FXCM server!
Dash is running on http://127.0.0.1:8050/

  • Serving Flask app ‘not main’
  • Debug mode: on
    Exception in thread Thread-7 (run_server):
    Traceback (most recent call last):
    File “C:\Users\tessa\AppData\Local\Programs\Python\Python310\lib\threading.py”, line 1009, in _bootstrap_inner
    self.run()
    File “C:\Users\tessa\AppData\Local\Programs\Python\Python310\lib\threading.py”, line 946, in run
    self._target(*self._args, **self._kwargs)
    File “C:\Users\tessa\envs_python\deepQlearn\lib\site-packages\dash\dash.py”, line 2132, in run_server
    self.run(*args, **kwargs)
    File “C:\Users\tessa\envs_python\deepQlearn\lib\site-packages\dash\dash.py”, line 1979, in run
    self.server.run(host=host, port=port, debug=debug, **flask_run_options)
    File “C:\Users\tessa\envs_python\deepQlearn\lib\site-packages\flask\app.py”, line 1188, in run
    run_simple(t.cast(str, host), port, self, **options)
    File “C:\Users\tessa\envs_python\deepQlearn\lib\site-packages\werkzeug\serving.py”, line 1090, in run_simple
    run_with_reloader(
    File “C:\Users\tessa\envs_python\deepQlearn\lib\site-packages\werkzeug_reloader.py”, line 427, in run_with_reloader
    signal.signal(signal.SIGTERM, lambda *args: sys.exit(0))
    File “C:\Users\tessa\AppData\Local\Programs\Python\Python310\lib\signal.py”, line 47, in signal
    handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
    ValueError: signal only works in main thread of the main interpreter

I think it’s because Dash is not compatible with multi threading

Ok, so question.

Why exactly are you trying to use Dash in a multi-thread?

Also, the figurewidget is not associated with Dash, but python only.

Your real-time data of stocks can be handled a couple of ways with Dash callbacks.

Because I have 2 process that works separatly in my code :

  • the first retrieves datas from market

  • the second displays these datas (Dash)

If I use Dash in a single thread, app.server_run() will block my code and I would not be able to retrieve more datas.

Also, I tried Figure widget in my code but it’s not displaying anything.

I’m probably using Dash in the wrong way, do you have a better way to do?

With Dash, you can use dcc.Interval to perform the same task as you while and return the data that way. :slight_smile:

https://dash.plotly.com/dash-core-components/interval

Okey I get it!
If I make all my code inside callbacks method it works.

app = Dash('not main')
app.layout = html.Div([
    html.H4('Apple stock candlestick chart'),

    dcc.Graph(id="graph"),

    dcc.Interval(
        id='interval-component',
        interval=10*1000, # in milliseconds
        n_intervals=0
    )
])
    
df_fixed_length = 50 

ACCESS_TOKEN = 'xxx'
conn = fxcmpy.fxcmpy(access_token=ACCESS_TOKEN, log_level='debug', server='demo', log_file='log.txt')
print("Connected to FXCM server!")
df = conn.get_candles('EUR/USD', period='m1', number=50)

fig = go.Figure(
    go.Candlestick(
        x=df.index,
        open=df['bidopen'],
        high=df['bidhigh'],
        low=df['bidlow'],
        close=df['bidclose']
    )
)

@app.callback(Output("graph", "figure"), Input('interval-component', 'n_intervals'))
def display_candlestick(n_intervals):
    global df
    print('n_interval:', n_intervals)
    if 0 < n_intervals and df_fixed_length < len(df):
        fig.add_trace(
            go.Candlestick(
                x=[df.index[-1]],
                open=[df['bidopen'].iloc[-1]],
                high=[df['bidhigh'].iloc[-1]],
                low=[df['bidlow'].iloc[-1]],
                close=[df['bidclose'].iloc[-1]]
        ))
        df = df.drop(df.index[0], axis=0)

    nbr_positions = len(conn.get_open_positions())
    if nbr_positions == 0:
        current_time = dt.datetime.now()
        if current_time.minute % 1 == 0 :
            df_new_price_value = conn.get_candles('EUR/USD', period='m1', number=1)
            if(df_new_price_value.index[-1] != df.index[-1]):
                df = pd.concat([df, df_new_price_value], axis = 0)


    return fig


app.run_server(debug=True)

Thanks

2 Likes

Good job! Glad you got it working.

2 Likes