Python+Dash快速web應(yīng)用開發(fā)——頁面布局篇

?本文示例代碼已上傳至我的
?Github倉庫https://github.com/CNFeffery/DataScienceStudyNotes
1 簡介
這是我的系列教程「Python+Dash快速web應(yīng)用開發(fā)」的第二期,在上一期中,我?guī)ьI(lǐng)大家認識了什么是Dash,Dash可以做什么,以及Dash中最基本的一些概念,而今天開始,我將開始帶領(lǐng)大家正式學(xué)習(xí)有關(guān)Dash的實用知識,以及各種奇淫巧技??~

今天的文章,我將帶大家學(xué)習(xí)Dash中「頁面布局」的先進方法,通過今天的文章,你將學(xué)會以非常簡單的方式實現(xiàn)現(xiàn)代化的頁面布局,下面讓我們開始吧~
2 為Dash應(yīng)用設(shè)計頁面布局
我們都知道,一個好的網(wǎng)頁設(shè)計通常都需要編寫css甚至js來定制前端內(nèi)容,譬如非常流行的bootstrap框架。

但我們既然想使用Dash來搭建web應(yīng)用,很大的一個原因是不熟悉或者不想寫繁瑣的前端代碼,而Dash的第三方拓展庫中就有這么一個Python庫——dash-bootstrap-components,借助它,我們就可以純Python編程調(diào)用到?bootstrap框架中的諸多特性來讓我們的web應(yīng)用頁面更美觀。
首先需要通過pip install dash-bootstrap-components來安裝它,安裝完成之后,我們來驗證一下是否可以正常使用,推薦以import dash_bootstrap_components as dbc的方式導(dǎo)入:
?app1.py
?
import?dash
import?dash_bootstrap_components?as?dbc
app?=?dash.Dash(
????__name__,
????#?從國內(nèi)可順暢訪問的cdn獲取所需的原生bootstrap對應(yīng)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()
執(zhí)行后打開所提示的網(wǎng)址,看到下列信息就說明安裝成功:

這里我們使用到dash.Dash()中的參數(shù)external_stylesheets,用于引入外部的css文件,有了這些補充進來的css,我們才得以實現(xiàn)更多彩的樣式,而除了上述填入url的方式之外,我更推薦的方式是在我們的Dash應(yīng)用.py文件同級目錄創(chuàng)建文件夾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()

這時在Dash頁面抓包可以看到對應(yīng)bootstrap.min.css的url信息指向域名下的對應(yīng)目錄:

這種方式最穩(wěn)妥,不受網(wǎng)絡(luò)波動影響,推薦大家養(yǎng)成好習(xí)慣。
在測試完dash-bootstrap-components的可用性之后,接下來我們就開始學(xué)習(xí)構(gòu)造頁面布局。
2.1 認識Container()、Row()與Col()
「Container()」
dash-bootstrap-components封裝了bootstrap框架中的「網(wǎng)格系統(tǒng)」,我們在使用它進行布局時,首先要了解的是組件Container(),它是我們組織頁面元素的容器,其參數(shù)fluid默認為False,會以兩邊填充空白區(qū)域的方式居中其內(nèi)部嵌套的子元素:
?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設(shè)置為True
????????dbc.Container(
????????????[
????????????????dcc.Dropdown(),
????????????????'測試',
????????????????dcc.Dropdown()
????????????],
????????????fluid=True
????????)
????]
)
if?__name__?==?"__main__":
????app.run_server()

可以看到,第一個Container()部分呈現(xiàn)出兩邊空白填充中間居中的形式,而第二個則充滿了整個水平方向。
「Row()與Col()」
在上面所介紹的Container()之內(nèi),我們就可以按照bootstrap的網(wǎng)格系統(tǒng)進行內(nèi)容的排布:「行」嵌套「列」,再向「列」內(nèi)嵌套各種部件。
而所謂的網(wǎng)格系統(tǒng)指的是每個Row()部件內(nèi)部分成寬度相等的「12」份,傳入的Col()部件具有參數(shù)width可以傳入「整數(shù)」來分配對應(yīng)數(shù)量的寬度,如下例:
?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()

可以看到當(dāng)Row()部件下所有Col()部件寬度之和為12時是正好充滿的,當(dāng)寬度之和不足12時剩余的寬度會被空出來,而寬度之和若大于12,則會把導(dǎo)致寬度溢出的Col()部件擠到下一行中,所以我們在利用這種網(wǎng)格系統(tǒng)排布網(wǎng)頁元素時要注意規(guī)范。
而「行部件」也是可以嵌套到上一級「列部件」中的,因此如果你覺得12份不夠自己實現(xiàn)更精確的寬度分配,就可以寫個嵌套,實現(xiàn)固定寬度下再次劃分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()

在get到這一小節(jié)的知識點后,我們就可以更規(guī)矩地編寫頁面內(nèi)容,譬如寫出下面這樣的調(diào)查問卷就比較輕松(受限于篇幅,下面例子對應(yīng)的app6.py不便放出代碼,你可以在文章開頭的Github倉庫對應(yīng)路徑找到它):
?app6.py
?

2.2 Row()與Col()部件的進階設(shè)置
通過上一小節(jié)的例子,想必你已經(jīng)學(xué)習(xí)到如何在Dash中編排出bootstrap網(wǎng)格系統(tǒng)風(fēng)格的頁面,而為了在已初步編排好的網(wǎng)頁基礎(chǔ)上做更多實用優(yōu)化,dash-bootstrap-components還為Row()與Col()部件提供了一些微調(diào)布局的參數(shù):
「利用order設(shè)定順序」
我們在前面為Col()部件所設(shè)定的width參數(shù)都只是1到12之間的整數(shù),其實它還可以接受字典輸入,從而拓展其功能,原先的整數(shù)寬度輸入就由width=n轉(zhuǎn)化為width={'size': n}。
除此之外,我們還可以添加order鍵參數(shù)來為同一個Row()下的部件設(shè)置順序,接受三種輸入:'first'表示置于當(dāng)前行第一列,'last'表示置于當(dāng)前行最后一列,而1到12的整數(shù)則可以直接以序號編排列部件順序。
結(jié)合下面這個簡單的例子理解這部分內(nèi)容:
?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參數(shù)對列部件順序的影響:

「利用offset設(shè)置偏移」
列部件的width參數(shù)字典中還可以使用鍵值對參數(shù)offset,傳入1到12的整數(shù),它的作用是為對應(yīng)的Col()部件左側(cè)增加對應(yīng)寬度的位移,就像下面的例子一樣:
?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()部件加了輪廓線,可以看到效果非常直觀:

「設(shè)置水平對齊方式」
在前面的內(nèi)容中,我們在同一個Row()部件下組織的所有Col()部件,其順序都是從左到右一個緊貼下一個排布的,即使設(shè)置了offset參數(shù),也只是插空后緊貼。
但在很多頁面布局需求中需要對于同一行的多個列元素設(shè)置「對齊方式」,這在dash-bootstrap-components中可以通過對Row()部件設(shè)置參數(shù)justify來實現(xiàn),可選項有'start'、'center'、'end'、'between'以及'around'五種,每種產(chǎn)生的效果如下面的例子:
?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()

2.3 實際案例
通過對上面知識內(nèi)容的學(xué)習(xí),我們掌握了如何基于拓展庫dash-bootstrap-components,在Dash中實現(xiàn)bootstrap的網(wǎng)格系統(tǒng)。
下面我們來利用今天學(xué)到的知識點,搭建下圖所示的登錄頁面,其中涉及到一些還未給大家介紹的知識點,但很簡單,之后的課程會介紹,而涉及到一些額外的css的內(nèi)容我都已寫好注釋非常簡單~

對應(yīng)代碼如下:
?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設(shè)置高度
????????????????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設(shè)置文字居中
????????????????????),
????????????????????style={'margin':?'6px'}??#?利用css設(shè)置上下留白高度
????????????????),
????????????????dbc.Row(
????????????????????dbc.Col(
????????????????????????#?利用css實現(xiàn)圓角矩形效果
????????????????????????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',??#?設(shè)置背景顏色
????????????????'max-width':?'480px',??#?為Container部件設(shè)置最大寬度
????????????????'border-radius':?'12px'
????????????}
????????)
????]
)
if?__name__?==?'__main__':
????app.run_server()
以上就是本文的全部內(nèi)容,歡迎在評論區(qū)與我進行討論,「點贊」越多下一期更新越快哦??~

加入我們的知識星球【我們談?wù)摂?shù)據(jù)科學(xué)】
愛上數(shù)據(jù)分析!
· 推薦閱讀?·
Python+Dash快速web應(yīng)用開發(fā)——基礎(chǔ)概念篇

