根据relayoutData属性自动缩放虚线中的y轴

2024-04-18 09:29:06 发布

您现在位置:Python中文网/ 问答频道 /正文

我开发了一个应用程序,其中用户输入股票符号,然后图表根据该符号进行更新。当用户缩放或拖动图表时,dash在y轴缩放方面的默认行为有问题。破折号的默认设置如下:enter image description here 当我缩放时可以看到,y轴范围不会根据缩放区域的范围而改变。它有很多空白。 为了解决这个问题,我尝试了很多方法,包括设置autorange=True和这个SOlink。在我的研究之后,我在回调中使用了relayoutData。以下是修复此缩放/拖动问题的代码:

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
import plotly
import plotly.graph_objs as go
from plotly.subplots import make_subplots
import pandas as pd
import numpy as np


app = dash.Dash(__name__)

app.layout = html.Div(children=[
    dcc.Input(id='input_1', debounce=True),
    html.P('input can be a or b', style={'color':'white'}),
    dcc.Graph(id='chart', config={'displayModeBar': True})
])

@app.callback(
Output('chart', 'figure'),
[Input('input_1','value'), Input('chart','relayoutData')],
 State('chart','figure'),
)
def set_range(sym, relay, fig_state):
    context = dash.callback_context
    if context.triggered[0]['prop_id'] == 'input_1.value':      # When the user inputs the sym

        global_df = pd.read_csv('https://raw.githubusercontent.com/AmirForooghi/stocks_csv/master/two_syms.csv')
        df = global_df.loc[global_df.sym == sym]

        fig = make_subplots(rows=2, cols=1, row_heights=[0.8, 0.2], vertical_spacing=0, shared_xaxes=True)
        fig.add_trace(go.Candlestick(open=df['open'], high=df['high'], low=df['low'], close=df['close'],
                                     increasing_line_color='#0384fc', decreasing_line_color='#e8482c', name=sym,
                                     showlegend=True,
                                     ), row=1, col=1)
        fig.add_trace(go.Scatter(y=df['pct'], marker_color='#fae823', name='pct', hovertemplate=[]), row=2, col=1)
        fig.update_layout({'plot_bgcolor': "#21201f", 'paper_bgcolor': "#21201f", 'legend_orientation': "h"},
                          legend=dict(y=0, x=0),
                          font=dict(color='#dedddc'), hovermode='x unified', showlegend=False, autosize=False, dragmode='pan',
                          margin=dict(b=20, t=0, l=0, r=40)
                          )
        fig.update_xaxes(showgrid=False, zeroline=False, rangeslider_visible=False, showticklabels=False,
                         showspikes=True, spikemode='across', spikesnap='data', showline=True, linecolor='#969799',
                         spikedash='dash', spikecolor='#ebeae8', spikethickness=0.5, rangemode='normal'
                         )
        fig.update_yaxes(showgrid=False, zeroline=False, showticklabels=True, showspikes=False)
        fig.update_traces(hoverinfo='skip', xaxis='x')
        return fig

    if relay!= None:

        if 'dragmode' in relay and relay['dragmode'] == 'zoom': # I added this because without it the chart shows a weird behaviour of not choosing zoom.
            print('if condition 1 --> setting dragmode to zoom')
            fig_state['layout'].update(dragmode='zoom')
            raise PreventUpdate

        if 'xaxis.range[0]' in relay:
            print('if condition 2 --> return data within the xaxis range')
            start = int(relay['xaxis.range[0]'])
            end = int(relay['xaxis.range[1]'])

            data_length = len(fig_state['data'][0]['open'])
            if start > data_length or end < 0:
                raise PreventUpdate
            if start < 0:
                start = 0
            if end > data_length:
                end = data_length

            low = fig_state['data'][0]['low'][start:end]
            high = fig_state['data'][0]['high'][start:end]
            price_min = min(low)
            price_max = max(high)

            pct = fig_state['data'][1]['y'][start:end]
            pct_min = min(pct) - 2
            pct_max = max(pct) + 2

            fig_state['layout']['yaxis'].update(range=[price_min, price_max], autorange=False)
            fig_state['layout']['yaxis2'].update(range=[pct_min, pct_max], autorange=False)
            if fig_state['layout']['dragmode'] == 'zoom':
                fig_state['layout']["dragmode"] = 'pan'
            return fig_state

        if 'xaxis.autorange' in relay and relay['xaxis.autorange'] == True:
            print('if condition 3 --> return data after autorange')
            pct = fig_state['data'][1]['y']
            pct_min = min(pct) - 2
            pct_max = max(pct) + 2
            fig_state['layout']['yaxis2'].update(range=[pct_min, pct_max])
            return fig_state

    if fig_state != None:   # just in case of not returning the correct data, I don't want to update
        print('if condition 4 --> raise preventupdate error')
        raise PreventUpdate

    empty_fig = go.Figure()
    empty_fig.update_layout({'plot_bgcolor': "#21201f", 'paper_bgcolor': "#21201f"},
                              font=dict(color='#dedddc'), showlegend=False, dragmode='pan',
                              xaxis = dict(showgrid=False, zeroline=False, showticklabels=False),
                              yaxis = dict(showgrid=False, zeroline=False, showticklabels=False)
                            )
    return empty_fig


if __name__ == '__main__':
    app.run_server(debug=True)

我可以以某种方式修复此问题,如下所示: enter image description here

然而,我遇到了一个奇怪的问题。当我缩放时,如果光标意外地落在第二个子图中,浏览器中将出现错误。 enter image description here 错误 enter image description here

我在代码中添加了几个print语句,以查看引擎盖下发生了什么。当我缩放并且光标不跟踪到第二个子地块时,代码将打印以下内容:

如果条件1-->;将dragmode设置为缩放

如果条件2-->;返回xaxis范围内的数据

当光标落在第二个子图中时,会显示以下内容:

如果条件1-->;将dragmode设置为缩放

如果条件2-->;返回xaxis范围内的数据

如果条件1-->;将dragmode设置为缩放

它正确返回数据,然后返回到条件1。 有什么办法可以解决这个问题吗?或者至少以某种方式捕捉错误并提出更新。这是一个非常奇怪的问题。因为我想把它用于商业用途,所以我希望它是完美的

这是我使用的CSS文件,以防你需要复制精确的设置。它需要大量的编辑,它不是一个完美的,但它是与此代码一起使用的

html{
    height:100%;
    width:100%;
}

body {
    background-color: #21201f;
}

#input_1, #input_2, #input_3{
    color:#e3e6e4;
    background-color: #21201f;
    /*position: fixed;*/
    width: 60px;
    height: 14px;
    top: 2%;
    border-width: 1px;
    caret-color: transparent
}

#input_1{
    left: 1%;
}

#input_2{
    left: 35%;

}

#input_3{
    left: 68%;
}

#chart {
    position: fixed;
    height: 89%;
    width: 100%;
    top: 13%;
    bottom: 2%;
    /*color: green;
    font-size: 30px;*/
}


#detail_1, #detail_2, #detail_3{
    height:1%;
    color:#e3e6e4 ;
    position: fixed;
    font-size: 14px;
}

#detail_1{
    top: 0;
    left: 1%;
}

#detail_2{
    top: 0;
    left: 35%;

}

#detail_3{
    top: 0;
    left: 68%;
}

Tags: importfalsetrueinputdataiffigupdate