Dash 布局是个谜——我感觉我在用 Lisp 编程

Dash layout is a mystery - I feel like I am programming in Lisp

我看过 this 文档,但我仍然不明白如何操作破折号布局。

我这样初始化dash

from datetime import datetime as dt
today = datetime.today()

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

我想安排一个破折号 layout 以便有:

A header on the very top
A dropdown, date picker, and another drop down, all on the same row without pusing into the header
A Table, that is a fixed height and has a scroll bar (not all data displayed have to scroll)
A Graph at the end

如何实现:

++++++++++++++++++++++++++++++++++++
+             Header               +
+ dropdown1 datepicker dropdown2   +
+ Table (extends all the way right)+
+ Graph (extends all the way right)+
++++++++++++++++++++++++++++++++++++

现在,显示是这样的,每个 component 一个接一个地堆叠,dropdown 从左到右一直延伸,datepicker尺寸有限,table和graph从左到右延伸。

编辑 1

Yaakov 下面的解决方案解决了我的几个问题,即组件现在大小合理,table 不再占据组件下方的整个页面。

但仍然存在一个问题,除了 table 之外的组件都向上推入 header,并且它们一个接一个地堆叠,而不是 [=73] =].这是我的破折号代码:

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.css.config.serve_locally = False
app.scripts.config.serve_locally = True

colors = {
    'background': '#111111',
    'text': '#7FDBFF'
}

app.layout = html.Div(style={'backgroundColor': colors['background']}, children=[
    html.H1(
            'Header Title',
            style={'color': 'orange', 'text-align': 'center'},     
        ),

    html.Div([ 
    dcc.Dropdown(
        id='dropdown1',
        options=[
            {'label': 'SPX', 'value': 'SPX'},
            {'label': 'AAPL', 'value': 'AAPL'},
            {'label': 'IBM', 'value': 'IBM'}
        ],
        value='SPX',
        style = {'display':'blocl', 'width':'33%', 'margin-left':'0', 'margin-right':'0'}
    )],
    ),
    html.Div([
    dcc.DatePickerSingle(
        id='my-date-picker-single',
        min_date_allowed=dt(2004, 1, 2),
        max_date_allowed=today,
        date=today 
    ),
      html.Div(id='output-container-date-picker-single')
    ],
    ),
    html.Div([ 
    dcc.Dropdown(
        id='dropdown2',
        options=[
            {'label': '10:00', 'value': '10:00:00'},
            {'label': '10:30', 'value': '10:30:00'},
            {'label': '11:00', 'value': '11:00:00'},
            {'label': '11:30', 'value': '11:30:00'},
            {'label': '12:00', 'value': '12:00:00'},
            {'label': '12:30', 'value': '12:30:00'},
            {'label': '13:00', 'value': '13:00:00'},
            {'label': '13:30', 'value': '13:30:00'},
            {'label': '14:00', 'value': '14:00:00'},
            {'label': '14:30', 'value': '14:30:00'},
            {'label': '15:00', 'value': '15:00:00'}
        ],
        style = {'display':'blocl', 'width':'33%', 'margin-left':'0', 'margin-right':'0'}
    )],
    ),
    html.Div([
     dash_table.DataTable(
          id='datatable-interactivity',
          columns=[{"name": i, "id": i} for i in df.columns],
          #fixed_rows={ 'headers': True, 'data': 0 },
          data=df.to_dict("rows"),
          page_current=0,
          page_size=0,
          page_action='custom',
          style_cell_conditional=[
        {'if': {'column_id': 'pSpot'},
         'width': '50px', 'textAlign': 'center'},
        {'if': {'column_id': 'quoteDatetime'},
         'width': '175px', 'textAlign': 'center'},
        {'if': {'column_id': 'underlyingSymbol'},
         'width': '50px', 'textAlign': 'center'},
        {'if': {'column_id': 'expiration'},
         'width': '175px', 'textAlign': 'center'},
        {'if': {'column_id': 'strike'},
         'width': '60px', 'textAlign': 'center'},
        {'if': {'column_id': 'optionType'},
         'width': '50px', 'textAlign': 'center'},
        {'if': {'column_id': 'bid'},
         'width': '100px'},
        {'if': {'column_id': 'ask'},
         'width': '100px'},
        {'if': {'column_id': 'underlyingBid'},
         'width': '130px',  'textAlign': 'center'},
        {'if': {'column_id': 'underlyingAsk'},
         'width': '130px',  'textAlign': 'center'},
        {'if': {'column_id': 'iv'},
         'width': '85px'},
        {'if': {'column_id': 'id'},
         'width': '155px'},
        ],
        style_data_conditional=[
        {
            'if': {'column_id': 'optionType',
                'filter_query': '{optionType} eq C'
                },
            'backgroundColor': '#3D9970',
            'color': 'white',
        }
        ],
        style_header={
          'backgroundColor': 'rgb(230, 230, 230)',
          'fontWeight': 'bold'
        },
    ),

    html.Div(id='datatable-interactivity-container')

    ],
    style={'overflowY': 'scroll', 'height': '50vh'}
    ),
])

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

编辑 2

这几乎行得通。控件是 side-by-side。他们也不会侵犯 Header。但是,当我单击 side-by-side 中的任何组件时,我看不到选项,并且出现奇怪的行为,它们会折叠并返回到一个堆叠在另一个顶部(和幽灵图片所在位置):

app.layout = html.Div([
    html.Div(
        html.H1(
                'Header Title',
                style={'color': 'orange', 'text-align': 'center'},     
        )
    ),

    html.Div([ 
        dcc.Dropdown(
            id='dropdown1',
            options=[
                {'label': 'SPX', 'value': 'SPX'},
                {'label': 'AAPL', 'value': 'AAPL'},
                {'label': 'IBM', 'value': 'IBM'}
            ],
            value='SPX',
            style = {'display':'blocl', 'width':'33%', 'margin-left':'0', 'margin-right':'0'}
        ),

        dcc.DatePickerSingle(
            id='my-date-picker-single',
            min_date_allowed=dt(2004, 1, 2),
            max_date_allowed=today,
            date=today 
        ),

        dcc.Dropdown(
            id='dropdown2',
            options=[
                {'label': '10:00', 'value': '10:00:00'},
                {'label': '10:30', 'value': '10:30:00'},
                {'label': '11:00', 'value': '11:00:00'},
                {'label': '11:30', 'value': '11:30:00'},
                {'label': '12:00', 'value': '12:00:00'},
                {'label': '12:30', 'value': '12:30:00'},
                {'label': '13:00', 'value': '13:00:00'},
                {'label': '13:30', 'value': '13:30:00'},
                {'label': '14:00', 'value': '14:00:00'},
                {'label': '14:30', 'value': '14:30:00'},
                {'label': '15:00', 'value': '15:00:00'}
            ],
            style = {'display':'blocl', 'width':'33%', 'margin-left':'0', 'margin-right':'0'}
        )
    ], style={'columnCount': 3}
    ),

    html.Div([
     dash_table.DataTable(
          id='datatable-interactivity',
          columns=[{"name": i, "id": i} for i in df.columns],
          #fixed_rows={ 'headers': True, 'data': 0 },
          data=df.to_dict("rows"),
          page_current=0,
          page_size=0,
          page_action='custom',
          style_cell_conditional=[
            {'if': {'column_id': 'pSpot'},
            'width': '50px', 'textAlign': 'center'},
            {'if': {'column_id': 'quoteDatetime'},
            'width': '175px', 'textAlign': 'center'},
            {'if': {'column_id': 'underlyingSymbol'},
            'width': '50px', 'textAlign': 'center'},
            {'if': {'column_id': 'expiration'},
            'width': '175px', 'textAlign': 'center'},
            {'if': {'column_id': 'strike'},
            'width': '60px', 'textAlign': 'center'},
            {'if': {'column_id': 'optionType'},
            'width': '50px', 'textAlign': 'center'},
            {'if': {'column_id': 'bid'},
            'width': '100px'},
            {'if': {'column_id': 'ask'},
            'width': '100px'},
            {'if': {'column_id': 'underlyingBid'},
            'width': '130px',  'textAlign': 'center'},
            {'if': {'column_id': 'underlyingAsk'},
            'width': '130px',  'textAlign': 'center'},
            {'if': {'column_id': 'iv'},
            'width': '85px'},
            {'if': {'column_id': 'id'},
            'width': '155px'},
            ],
            style_data_conditional=[
            {
                'if': {'column_id': 'optionType',
                    'filter_query': '{optionType} eq C'
                    },
                'backgroundColor': '#3D9970',
                'color': 'white',
            }
        ],
        style_header={
          'backgroundColor': 'rgb(230, 230, 230)',
          'fontWeight': 'bold'
        },
    ),

    html.Div(id='datatable-interactivity-container')

    ],
    style={'overflowY': 'scroll', 'height': '50vh'}
    )
])

编辑 3

改变

'display' : 'inline-block'

在上面的 EDIT 2 中几乎可以工作!但是,当我单击组件时,下拉菜单不会越过 table,下方的 table 会向下移动以允许显示选项,然后在进行下拉选择后一切都会恢复原状。

编辑 4

Table 不是必需的,底部的任何控件都有效:

,
dcc.Graph(
        id='example-graph-2',
        figure={
            'data': [
                {'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
                {'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montréal'},
            ],
            'layout': {
                'plot_bgcolor': colors['background'],
                'paper_bgcolor': colors['background'],
                'font': {
                    'color': colors['text']
                }
            }
        }
    )

编辑 5

用图表代替 table 的完整示例。没有区别:

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.css.config.serve_locally = False
app.scripts.config.serve_locally = True

#for css in external_css:
#    app.css.append_css({"external_url": css})

colors = {
    'background': '#111111',
    'text': '#7FDBFF'
}

app.layout = html.Div(children = [
    html.Div(
        html.H1(
                'Header Title',
                style={'color': 'orange', 'text-align': 'center'},     
        )
    ),

    html.Div([ 
        dcc.Dropdown(
            id='dropdown1',
            options=[
                {'label': 'SPX', 'value': 'SPX'},
                {'label': 'AAPL', 'value': 'AAPL'},
                {'label': 'IBM', 'value': 'IBM'}
            ],
            value='SPX',
            style = {'display':'inline-block', 'width':'32%', 'margin-left':'0', 'margin-right':'0'}
        ),

        dcc.Dropdown(
            id='dropdown2',
            options=[
                {'label': '10:00', 'value': '10:00:00'},
                {'label': '10:30', 'value': '10:30:00'},
                {'label': '11:00', 'value': '11:00:00'},
                {'label': '11:30', 'value': '11:30:00'},
                {'label': '12:00', 'value': '12:00:00'},
                {'label': '12:30', 'value': '12:30:00'},
                {'label': '13:00', 'value': '13:00:00'},
                {'label': '13:30', 'value': '13:30:00'},
                {'label': '14:00', 'value': '14:00:00'},
                {'label': '14:30', 'value': '14:30:00'},
                {'label': '15:00', 'value': '15:00:00'}
            ],
            value='15:00',
            style = {'display':'inline-block', 'width':'32%', 'margin-left':'0', 'margin-right':'0'}
        ),

        dcc.DatePickerSingle(
            id='my-date-picker-single',
            min_date_allowed=dt(2004, 1, 2),
            max_date_allowed=today,
            date=today 
        ),

    ], style={'columnCount': 3}
    ),
    dcc.Graph(
        id='example-graph-2',
        figure={
            'data': [
                {'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
                {'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montréal'},
            ],
            'layout': {
                'plot_bgcolor': colors['background'],
                'paper_bgcolor': colors['background'],
                'font': {
                    'color': colors['text']
                }
            }
        }
    )
])


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

您可以在实例化时在每个 htmldcc 组件的 style 参数中进行调整。您可以在 app.py 文件中通过在实例化中包含这样的定义:

html.Div(
  id='dropdown1',
  children='this is a dropdown',
  style = {'display':'blocl', 'width':'33%', 'margin-left':'0', 'margin-right':'0'}
)

对于您的 table,您可以将 y-overflow 设置为 scroll,并将您的身高设置为 50vh(视图高度的 50%):

style={'overflowY': 'scroll', 'height': '50vh'}

编辑:

(回应编辑 4) 对 html 对象的 style 进行以下更改以更正野性四溢:

    html.Div(
        children = [
            dcc.Dropdown(
                id='dropdown1',
                options=[
                    {'label': 'SPX', 'value': 'SPX'},
                    {'label': 'AAPL', 'value': 'AAPL'},
                    {'label': 'IBM', 'value': 'IBM'}
                ],
                value='SPX',
                style = {'width':'30%', 'margin':'0 0 0'}
            ),

            dcc.DatePickerSingle(
                id='my-date-picker-single',
                # min_date_allowed=dt(2004, 1, 2),
                # max_date_allowed=today,
                # date=today
                style = {'width':'30%', 'margin':'0 0 0'}
            ),
            dcc.Dropdown(
                id='dropdown2',
                options=[
                    {'label': '10:00', 'value': '10:00:00'},
                    {'label': '10:30', 'value': '10:30:00'},
                    # And so on....
                ],
                style = {'width':'30%', 'margin':'0 0 0'}
            )
        ],
    # Link to CSS styles https://www.w3schools.com/css/css3_flexbox.asp
    style={'display':'flex', 'flex-direction': 'row'}
    ),