(資料科學學習手札103)Python+Dash快速web應用開發——頁面佈局篇

費弗裡發表於2021-01-15

本文示例程式碼已上傳至我的Github倉庫https://github.com/CNFeffery/DataScienceStudyNotes

1 簡介

   這是我的系列教程Python+Dash快速web應用開發的第二期,在上一期中,我帶領大家認識了什麼是DashDash可以做什麼,以及Dash中最基本的一些概念,而今天開始,我將開始帶領大家正式學習有關Dash的實用知識,以及各種奇淫巧技?~

(資料科學學習手札103)Python+Dash快速web應用開發——頁面佈局篇
圖1

  今天的文章,我將帶大家學習Dash頁面佈局的先進方法,通過今天的文章,你將學會以非常簡單的方式實現現代化的頁面佈局,下面讓我們開始吧~

2 為Dash應用設計頁面佈局

  我們都知道,一個好的網頁設計通常都需要編寫css甚至js來定製前端內容,譬如非常流行的bootstrap框架。

(資料科學學習手札103)Python+Dash快速web應用開發——頁面佈局篇
圖2

  但我們既然想使用Dash來搭建web應用,很大的一個原因是不熟悉或者不想寫繁瑣的前端程式碼,而Dash的第三方擴充庫中就有這麼一個Python庫——dash-bootstrap-components,藉助它,我們就可以純Python程式設計呼叫到 bootstrap框架中的諸多特性來讓我們的web應用頁面更美觀。

  首先需要通過pip install dash-bootstrap-components來安裝它,安裝完成之後,我們來驗證一下是否可以正常使用,推薦以import dash_bootstrap_components as dbc的方式匯入:

app1.py

import dash
import dash_bootstrap_components as dbc

app = dash.Dash(
    __name__,
    # 從國內可順暢訪問的cdn獲取所需的原生bootstrap對應css
    external_stylesheets=['https://cdn.staticfile.org/twitter-bootstrap/4.5.2/css/bootstrap.min.css']
)

app.layout = dbc.Alert(
    "你好,dash_bootstrap_components!"
)

if __name__ == "__main__":
    app.run_server()

  執行後開啟所提示的網址,看到下列資訊就說明安裝成功:

(資料科學學習手札103)Python+Dash快速web應用開發——頁面佈局篇
圖3

  這裡我們使用到dash.Dash()中的引數external_stylesheets,用於引入外部的css檔案,有了這些補充進來的css,我們才得以實現更多彩的樣式,而除了上述填入url的方式之外,我更推薦的方式是在我們的Dash應用.py檔案同級目錄建立資料夾assets,放在這個目錄中的檔案會被Dash自動掃描到:

app2.py

import dash
import dash_bootstrap_components as dbc

app = dash.Dash(
    __name__,
    # 直接填寫assets下css檔案路徑+檔名
    external_stylesheets=['css/bootstrap.min.css']
)

app.layout = dbc.Alert(
    "你好,dash_bootstrap_components!"
)

if __name__ == "__main__":
    app.run_server()
(資料科學學習手札103)Python+Dash快速web應用開發——頁面佈局篇
圖4

  這時在Dash頁面抓包可以看到對應bootstrap.min.css的url資訊指向域名下的對應目錄:

(資料科學學習手札103)Python+Dash快速web應用開發——頁面佈局篇
圖5

  這種方式最穩妥,不受網路波動影響,推薦大家養成好習慣。

  在測試完dash-bootstrap-components的可用性之後,接下來我們就開始學習構造頁面佈局。

2.1 認識Container()、Row()與Col()

  • Container()

  dash-bootstrap-components封裝了bootstrap框架中的網格系統,我們在使用它進行佈局時,首先要了解的是元件Container(),它是我們組織頁面元素的容器,其引數fluid預設為False,會以兩邊填充空白區域的方式居中其內部巢狀的子元素:

app3.py

import dash
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash(
    __name__,
    external_stylesheets=['css/bootstrap.min.css']
)

app.layout = html.Div(
    [
        # fluid預設為False
        dbc.Container(
            [
                dcc.Dropdown(),
                '測試',
                dcc.Dropdown()
            ]
        ),

        html.Hr(), # 水平分割線

        # fluid設定為True
        dbc.Container(
            [
                dcc.Dropdown(),
                '測試',
                dcc.Dropdown()
            ],
            fluid=True
        )
    ]
)

if __name__ == "__main__":
    app.run_server()
(資料科學學習手札103)Python+Dash快速web應用開發——頁面佈局篇
圖6

  可以看到,第一個Container()部分呈現出兩邊空白填充中間居中的形式,而第二個則充滿了整個水平方向。

  • Row()與Col()

  在上面所介紹的Container()之內,我們就可以按照bootstrap的網格系統進行內容的排布:巢狀,再向內巢狀各種部件。

  而所謂的網格系統指的是每個Row()部件內部分成寬度相等的12份,傳入的Col()部件具有引數width可以傳入整數來分配對應數量的寬度,如下例:

app4.py

import dash
import dash_bootstrap_components as dbc

app = dash.Dash(
    __name__,
    external_stylesheets=['css/bootstrap.min.css']
)

app.layout = dbc.Container(
    [
        dbc.Row(dbc.Col('第一行'),
                style={
                    'background-color': 'lightgreen'
                }),
        dbc.Row(
            [
                dbc.Col('第二行第一列', width=6, style={'background-color': 'lightblue'}),
                dbc.Col('第二行第二列', width=6, style={'background-color': 'lightskyblue'})
            ]
        ),
        dbc.Row(
            [
                dbc.Col('第三行第一列', width=2, style={'background-color': 'HotPink'}),
                dbc.Col('第三行第二列', width=10, style={'background-color': 'IndianRed'})
            ]
        ),
        dbc.Row(
            [
                dbc.Col('第四行第一列', width=2, style={'background-color': 'HotPink'}),
                dbc.Col('第四行第二列', width=2, style={'background-color': 'IndianRed'}),
                dbc.Col('第四行第三列', width=2, style={'background-color': 'HotPink'})
            ]
        ),
        dbc.Row(
            [
                dbc.Col('第五行第一列', width=2, style={'background-color': 'LightSteelBlue'}),
                dbc.Col('第五行第二列', width=11, style={'background-color': 'MistyRose'}),
            ]
        )
    ]
)

if __name__ == "__main__":
    app.run_server()
(資料科學學習手札103)Python+Dash快速web應用開發——頁面佈局篇
圖7

  可以看到當Row()部件下所有Col()部件寬度之和為12時是正好充滿的,當寬度之和不足12時剩餘的寬度會被空出來,而寬度之和若大於12,則會把導致寬度溢位的Col()部件擠到下一行中,所以我們在利用這種網格系統排佈網頁元素時要注意規範。

  而行部件也是可以巢狀到上一級列部件中的,因此如果你覺得12份不夠自己實現更精確的寬度分配,就可以寫個巢狀,實現固定寬度下再次劃分12份,就像下面例子中我們:

app5.py

import dash
import dash_bootstrap_components as dbc

app = dash.Dash(
    __name__,
    external_stylesheets=['css/bootstrap.min.css']
)

app.layout = dbc.Container(
    [
        dbc.Row(dbc.Col('第一行'),
                style={
                    'background-color': 'lightgreen'
                }),
        dbc.Row(
            [
                dbc.Col('第二行第一列', width=6, style={'background-color': 'lightblue'}),
                dbc.Col(
                    dbc.Row(
                        [
                            dbc.Col('巢狀1', width=6, style={'background-color': 'Moccasin'}),
                            dbc.Col('巢狀2', width=3, style={'background-color': 'lightskyblue'}),
                            dbc.Col('巢狀3', width=3, style={'background-color': 'Moccasin'}),
                        ]
                    ),
                    width=6,
                    style={'background-color': 'lightskyblue'})
            ]
        )
    ]
)

if __name__ == "__main__":
    app.run_server()
(資料科學學習手札103)Python+Dash快速web應用開發——頁面佈局篇
圖8

  在get到這一小節的知識點後,我們就可以更規矩地編寫頁面內容,譬如寫出下面這樣的調查問卷就比較輕鬆(受限於篇幅,下面例子對應的app6.py不便放出程式碼,你可以在文章開頭的Github倉庫對應路徑找到它):

app6.py

(資料科學學習手札103)Python+Dash快速web應用開發——頁面佈局篇
圖9

2.2 Row()與Col()部件的進階設定

  通過上一小節的例子,想必你已經學習到如何在Dash中編排出bootstrap網格系統風格的頁面,而為了在已初步編排好的網頁基礎上做更多實用優化,dash-bootstrap-components還為Row()Col()部件提供了一些微調佈局的引數:

  • 利用order設定順序

  我們在前面為Col()部件所設定的width引數都只是1到12之間的整數,其實它還可以接受字典輸入,從而擴充其功能,原先的整數寬度輸入就由width=n轉化為width={'size': n}

  除此之外,我們還可以新增order鍵引數來為同一個Row()下的部件設定順序,接受三種輸入:'first'表示置於當前行第一列,'last'表示置於當前行最後一列,而1到12的整數則可以直接以序號編排列部件順序。

  結合下面這個簡單的例子理解這部分內容:

app7.py

import dash
import dash_bootstrap_components as dbc
import dash_html_components as html

app = dash.Dash(
    __name__,
    external_stylesheets=['css/bootstrap.min.css']
)

app.layout = html.Div(
    dbc.Container(
        [
            html.Br(),
            html.Br(),
            html.Br(),
            dbc.Row(
                [
                    dbc.Col('1', width=2, style={'background-color': 'lightblue'}),
                    dbc.Col('2', width=2, style={'background-color': 'lightskyblue'}),
                    dbc.Col('3', width=2, style={'background-color': '#e88b00'}),
                    dbc.Col('4', width=2, style={'background-color': '#8c8c8c'})
                ]
            ),
            html.Br(),
            dbc.Row(
                [
                    dbc.Col('order=last', width={'size': 2, 'order': 'last'}, style={'background-color': 'lightblue'}),
                    dbc.Col('order=2', width={'size': 2, 'order': 2}, style={'background-color': 'lightskyblue'}),
                    dbc.Col('order=1', width={'size': 2, 'order': 1}, style={'background-color': '#e88b00'}),
                    dbc.Col('order=first', width={'size': 2, 'order': 'first'}, style={'background-color': '#8c8c8c'})
                ]
            )
        ]
    )
)

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

  可以很直觀地看出order引數對列部件順序的影響:

(資料科學學習手札103)Python+Dash快速web應用開發——頁面佈局篇
圖10
  • 利用offset設定偏移

  列部件的width引數字典中還可以使用鍵值對引數offset,傳入1到12的整數,它的作用是為對應的Col()部件左側增加對應寬度的位移,就像下面的例子一樣:

app8.py

import dash
import dash_bootstrap_components as dbc
import dash_html_components as html

app = dash.Dash(
    __name__,
    external_stylesheets=['css/bootstrap.min.css']
)

app.layout = html.Div(
    dbc.Container(
        [
            html.Br(),
            html.Br(),
            html.Br(),
            dbc.Row(
                [
                    dbc.Col('1', width=2, style={'background-color': 'lightblue'}),
                    dbc.Col('2', width=2, style={'background-color': 'lightskyblue'}),
                    dbc.Col('3', width=2, style={'background-color': '#e88b00'}),
                    dbc.Col('4', width=2, style={'background-color': '#8c8c8c'})
                ],
                style={'border': '1px solid black'}
            ),
            html.Br(),
            dbc.Row(
                [
                    dbc.Col('offset=1', width={'size': 2, 'offset': 1}, style={'background-color': 'lightblue'}),
                    dbc.Col('offset=2', width={'size': 2, 'offset': 2}, style={'background-color': 'lightskyblue'}),
                    dbc.Col('3', width=2, style={'background-color': '#e88b00'}),
                    dbc.Col('offset=1', width={'size': 2, 'offset': 1}, style={'background-color': '#8c8c8c'})
                ],
                style={'border': '1px solid black'}
            )
        ]
    )
)

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

  為了更明顯,我給每個Row()部件加了輪廓線,可以看到效果非常直觀:

(資料科學學習手札103)Python+Dash快速web應用開發——頁面佈局篇
圖11
  • 設定水平對齊方式

  在前面的內容中,我們在同一個Row()部件下組織的所有Col()部件,其順序都是從左到右一個緊貼下一個排布的,即使設定了offset引數,也只是插空後緊貼。

  但在很多頁面佈局需求中需要對於同一行的多個列元素設定對齊方式,這在dash-bootstrap-components中可以通過對Row()部件設定引數justify來實現,可選項有'start''center''end''between'以及'around'五種,每種產生的效果如下面的例子:

app9.py

import dash
import dash_bootstrap_components as dbc
import dash_html_components as html

app = dash.Dash(
    __name__,
    external_stylesheets=['css/bootstrap.min.css']
)

app.layout = html.Div(
    dbc.Container(
        [
            html.Br(),
            html.Br(),
            html.Br(),
            dbc.Row(
                [
                    dbc.Col('start', width=3, style={'border': '1px solid black'}),
                    dbc.Col('start', width=3, style={'border': '1px solid black'}),
                    dbc.Col('start', width=3, style={'border': '1px solid black'})
                ],
                justify='start'
            ),
            html.Br(),
            dbc.Row(
                [
                    dbc.Col('center', width=3, style={'border': '1px solid black'}),
                    dbc.Col('center', width=3, style={'border': '1px solid black'}),
                    dbc.Col('center', width=3, style={'border': '1px solid black'})
                ],
                justify='center'
            ),
            html.Br(),
            dbc.Row(
                [
                    dbc.Col('end', width=3, style={'border': '1px solid black'}),
                    dbc.Col('end', width=3, style={'border': '1px solid black'}),
                    dbc.Col('end', width=3, style={'border': '1px solid black'})
                ],
                justify='end'
            ),
            html.Br(),
            dbc.Row(
                [
                    dbc.Col('between', width=3, style={'border': '1px solid black'}),
                    dbc.Col('between', width=3, style={'border': '1px solid black'}),
                    dbc.Col('between', width=3, style={'border': '1px solid black'})
                ],
                justify='between'
            ),
            html.Br(),
            dbc.Row(
                [
                    dbc.Col('around', width=3, style={'border': '1px solid black'}),
                    dbc.Col('around', width=3, style={'border': '1px solid black'}),
                    dbc.Col('around', width=3, style={'border': '1px solid black'})
                ],
                justify='around'
            )
        ],
        # 為Container兩邊新增參考線
        style={'border-left': '1px solid red', 'border-right': '1px solid red'}
    )
)

if __name__ == '__main__':
    app.run_server()
(資料科學學習手札103)Python+Dash快速web應用開發——頁面佈局篇
圖12

2.3 實際案例

  通過對上面知識內容的學習,我們掌握瞭如何基於擴充庫dash-bootstrap-components,在Dash中實現bootstrap的網格系統。

  下面我們來利用今天學到的知識點,搭建下圖所示的登入頁面,其中涉及到一些還未給大家介紹的知識點,但很簡單,之後的課程會介紹,而涉及到一些額外的css的內容我都已寫好註釋非常簡單~

(資料科學學習手札103)Python+Dash快速web應用開發——頁面佈局篇
圖13

  對應程式碼如下:

app10.py

import dash
import dash_html_components as html
import dash_bootstrap_components as dbc

app = dash.Dash(
    __name__,
    external_stylesheets=['css/bootstrap.min.css']
)

app.layout = html.Div(
    [
        html.Br(),
        html.Br(),
        html.Br(),
        html.Br(),
        html.Br(),
        html.Br(),
        html.Br(),
        html.Br(),
        dbc.Container(
            [
                dbc.Row(style={'height': '30px'}),  # 利用css設定高度
                dbc.Row(
                    dbc.Col('Email address')
                ),
                dbc.Row(
                    dbc.Col(dbc.Input(placeholder='Enter email'))
                ),
                dbc.Row(
                    dbc.Col('Password')
                ),
                dbc.Row(
                    dbc.Col(dbc.Input(placeholder='Enter Password'))
                ),
                dbc.Row(
                    dbc.Col(
                        [
                            'By signing up you accept our ',
                            html.A('Terms Of Use', href='#')
                        ],
                        width={'size': 10, 'offset': 1},
                        style={'text-align': 'center'}  # 利用css設定文字居中
                    ),
                    style={'margin': '6px'}  # 利用css設定上下留白高度
                ),
                dbc.Row(
                    dbc.Col(
                        # 利用css實現圓角矩形效果
                        dbc.Button('LOGIN', style={'border-radius': '18px'}, block=True),
                        width={'size': 8, 'offset': 2},
                        style={'text-align': 'center'}
                    )
                ),
                dbc.Row(
                    [
                        dbc.Col(html.Hr()),
                        html.P('or', style={'text-align': 'center', 'margin': 0}),
                        dbc.Col(html.Hr())
                    ]
                ),
                dbc.Row(
                    dbc.Col(
                        dbc.Button(
                            'Signup using Google',
                            style={'border-radius': '18px'},
                            block=True,
                            outline=True
                        ),
                        width={'size': 8, 'offset': 2},
                        style={'text-align': 'center'}
                    )
                ),
                dbc.Row(
                    dbc.Col(
                        [
                            "Don't have account? ",
                            html.A('Sign up here', href='#')
                        ],
                        width={'size': 10, 'offset': 1},
                        style={'text-align': 'center'}
                    ),
                    style={'margin': '6px'}
                ),
                html.Br(),
            ],
            style={
                'background-color': '#ededef',  # 設定背景顏色
                'max-width': '480px',  # 為Container部件設定最大寬度
                'border-radius': '12px'
            }
        )
    ]
)

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

  以上就是本文的全部內容,歡迎在評論區與我進行討論,點贊越多下一期更新越快哦?~

相關文章