(資料科學學習手札112)Python+Dash快速web應用開發——表單控制元件篇(上)

費弗裡發表於2021-03-14

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

1 簡介

   這是我的系列教程Python+Dash快速web應用開發的第九期,在之前三期的教程中,我們針對Dash中經常會用到的一些靜態部件進行了較為詳細的介紹,從而get到在Dash應用中組織靜態內容的常用方法。

  而從今天的教程開始,我將帶大家來認識和學習Dash生態中非常實用的一些互動式部件,配合回撥函式,可以幫助我們構建一個形式豐富的可接受輸入,並反饋輸出的互動式應用,今天要介紹的互動部件為表單輸入類部件的基礎知識,下面來學習吧~

(資料科學學習手札112)Python+Dash快速web應用開發——表單控制元件篇(上)
圖1

2 Dash中常用的表單輸入類互動部件

  互動部件跟之前介紹的一系列靜態部件的區別在於它們不僅具有供使用者互動操作的特點,還承擔了接受使用者輸入,並傳遞這些輸入引數的作用。而網頁開發中,表單輸入類部件則是互動部件中最常用到的。

  在Dash生態中常用到的表單輸入類互動部件有:

2.1 輸入框部件Input()

  其實在之前的教程內容中我們已經使用過很多次輸入框部件Input()了,而我比較推薦使用的是dash_bootstrap_components中封裝的Input(),它相較於dash_core_components中自帶的Input()擁有更多特性。

  除了幾乎所有部件都具有的idclassName以及style引數之外,Input()中還有一個特殊的引數type,它的不同取值從根本上奠定了Input()的角色,常用的有:

  • text、password、search

  當Input()type引數取值為'text''password'以及'search'之一時,它分別扮演文字輸入框、密碼輸入框以及搜尋框等角色,也擁有了一些特別的常用引數&屬性:

  value屬性對應它當前的輸入值;

  placeholder用於設定未輸入時輸入框內的提示文字;

  maxLength用於設定最多可輸入的字元數量;

  n_submit用於記錄游標在輸入框內部時鍵盤Enter鍵被點按的次數;

  debounce設定為True時會強制每次使用者按下Enter鍵或點選其他部件時才同步value值給後臺Dash服務。

  validinvalid引數都接受Bool型引數,分別用來控制輸入框顯示正確狀態以及錯誤狀態,我們可以在檢查使用者名稱、密碼等是否正確時通過回撥輸出設定這些引數為True來告知使用者相關提示資訊。

  我們來通過下面的示例來直觀感受這些特性:

app1.py

import dash
import dash_bootstrap_components as dbc
import dash_html_components as html
from dash.dependencies import Input, Output

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        [
            dbc.Input(id='input-text',
                      placeholder='text模式,長度限制4',
                      type='text',
                      maxLength=4,
                      style={'width': '300px'}),
            html.P(id='output-text'),
            dbc.Input(id='input-password',
                      placeholder='password模式,繫結Enter鍵',
                      type='password',
                      style={'width': '300px'},
                      debounce=True),
            html.P(id='output-password'),
            dbc.Input(id='input-search',
                      placeholder='search模式,可快速清除內容',
                      type='search',
                      style={'width': '300px'}),
            html.P(id='output-search'),
        ],
        style={'margin-top': '100px'}
    )
)

@app.callback(
    Output('output-text', 'children'),
    Input('input-text', 'value')
)
def output_text(value):

    return value

@app.callback(
    Output('output-password', 'children'),
    [Input('input-password', 'value'),
     Input('input-password', 'n_submit')]
)
def output_password(value, n_submit):

    if value:

        return '密碼為:'+value+'  '+f'第{n_submit}次按下Enter'

    return dash.no_update

if __name__ == '__main__':
    app.run_server(debug=True)
(資料科學學習手札112)Python+Dash快速web應用開發——表單控制元件篇(上)
圖2
  • number、range

  當Input()部件的type屬性設定為'number'時,它便搖身一變成了數值輸入框,並擁有了一些特殊的引數&屬性:

  minmax引數用來約束數值輸入框的輸入值上下限;

  step引數用來設定數值輸入框右側上下箭頭點按一次後數值變化的步長

  而當type設定為range時就更有意思了,我們的Input()這時變成了一個滑桿,也是通過上述三個引數來限制範圍和拖動的步長值。

app2.py

import dash
import dash_bootstrap_components as dbc
import dash_html_components as html
from dash.dependencies import Input, Output

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        [
            dbc.Input(id='input-number',
                      placeholder='number模式',
                      type='number',
                      min=0,
                      max=100,
                      step=0.5,
                      style={'width': '300px'}),
            html.P(id='output-number'),
            dbc.Input(id='input-range',
                      placeholder='range模式',
                      type='range',
                      style={'width': '300px'},
                      min=0,
                      max=100,
                      step=10,),
            html.P(id='output-range')
        ],
        style={'margin-top': '100px'}
    )
)

@app.callback(
    Output('output-number', 'children'),
    Input('input-number', 'value')
)
def output_number(value):
    return value

@app.callback(
    Output('output-range', 'children'),
    Input('input-range', 'value')
)
def output_range(value):
    return value

if __name__ == '__main__':
    app.run_server(debug=True)
(資料科學學習手札112)Python+Dash快速web應用開發——表單控制元件篇(上)
圖3

2.2 下拉選擇部件Dropdown()

  接下來我們來深入學習之前也使用過很多次的下拉選擇部件Dropdown(),直接使用dash_core_components中的Dropdown()即可,它的主要屬性&引數有:

  options用於設定我們的下拉選擇部件中顯示的選項,傳入列表,列表每個元素為字典,必填鍵有:'label',用於設定對應選項顯示的標籤名稱;'value',對應當前選項的值,也是我們書寫回撥函式接受的輸入;'disabled',一般情況下不用設定,除非你想指定對應選項不可點選就設定為True;

  multi,bool型,用於設定是否允許多選;

  optionHeight,用於設定每個選項的顯示畫素高度,預設35;

  placeholder,同Input()同名引數;

  searchable,bool型,用於設定是否可以在輸入框中搜尋下拉選項;

  search_value,可用作回撥的輸入,記錄了使用者的搜尋內容;

  value,記錄使用者已選擇的選項,單選模式下為對應單個選項的'value'值,多選模式下為對應多個選項'value'值組成的列表;

app3.py

import dash
import dash_bootstrap_components as dbc
import dash_html_components as html
from dash.dependencies import Input, Output
import dash_core_components as dcc
import json

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        [
            dcc.Dropdown(
                id='dropdown-input-1',
                placeholder='單選',
                options=[
                    {'label': item, 'value': item}
                    for item in list('ABCD')
                ],
                style={
                    'width': '300px'
                }
            ),
            html.Pre(id='dropdown-output-1',
                     style={'background-color': '#d4d4d420',
                            'width': '300px'}),
            dcc.Dropdown(
                id='dropdown-input-2',
                placeholder='多選',
                multi=True,
                options=[
                    {'label': item, 'value': item}
                    for item in list('ABCD')
                ],
                style={
                    'width': '300px'
                }
            ),
            html.Pre(id='dropdown-output-2',
                     style={'background-color': '#d4d4d420',
                            'width': '300px'})
        ],
        style={'margin-top': '100px'}
    )
)

@app.callback(
    Output('dropdown-output-1', 'children'),
    Input('dropdown-input-1', 'value')
)
def dropdown_output_1(value):
    if value:
        return json.dumps(value, indent=4)

    return dash.no_update

@app.callback(
    Output('dropdown-output-2', 'children'),
    Input('dropdown-input-2', 'value')
)
def dropdown_output_2(value):
    if value:
        return json.dumps(value, indent=4)

    return dash.no_update

if __name__ == '__main__':
    app.run_server(debug=True)
(資料科學學習手札112)Python+Dash快速web應用開發——表單控制元件篇(上)
圖4

2.3 單選框與核取方塊

  我們分別可以使用dash_bootstrap_components中的RadioItemsChecklist來建立單選框與核取方塊:

  • 單選框RadioItems

  單選框的特點是我們只能在其展示的一組選項中選擇1項。

  它的引數options格式同Dropdown()

  inline引數設定為True時會橫向佈局所有選項;

  switch設定為True時會將每個選項樣式切換為開關;

app4.py

import dash
import dash_bootstrap_components as dbc
import dash_html_components as html
from dash.dependencies import Input, Output
import dash_core_components as dcc
import json

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        [
            dbc.RadioItems(
                id='radio-items-input',
                inline=True,
                switch=True,
                options=[
                    {'label': item, 'value': item}
                    for item in list('ABCD')
                ],
                style={
                    'width': '300px'
                }
            ),
            html.P(id='radio-items-output')
        ],
        style={'margin-top': '100px'}
    )
)

@app.callback(
    Output('radio-items-output', 'children'),
    Input('radio-items-input', 'value')
)
def radio_items_output(value):

    if value:
        return '已選擇:'+value

    return dash.no_update

if __name__ == '__main__':
    app.run_server(debug=True)
(資料科學學習手札112)Python+Dash快速web應用開發——表單控制元件篇(上)
圖5
  • 核取方塊Checklist

  與單選框相對的,是核取方塊,它的引數與RadioItems完全一致,唯一不同的是它是可以多選的:

app5.py

import dash
import dash_bootstrap_components as dbc
import dash_html_components as html
from dash.dependencies import Input, Output
import dash_core_components as dcc
import json

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        [
            dbc.Checklist(
                id='check-list-input',
                inline=True,
                options=[
                    {'label': item, 'value': item}
                    for item in list('ABCD')
                ],
                style={
                    'width': '300px'
                }
            ),
            html.P(id='check-list-output')
        ],
        style={'margin-top': '100px'}
    )
)

@app.callback(
    Output('check-list-output', 'children'),
    Input('check-list-input', 'value')
)
def check_list_output(value):

    if value:
        return '已選擇:'+'、'.join(value)

    return dash.no_update

if __name__ == '__main__':
    app.run_server(debug=True)
(資料科學學習手札112)Python+Dash快速web應用開發——表單控制元件篇(上)
圖6

  而除了上述兩種供使用者對多個選項進行單選或多選的部件之外,dash_bootstrap_components中還有可以建立單個選擇部件的RadioButtonCheckbox,它們只能進行勾選操作,對應回撥用的的輸入值為checked,是個Bool型屬性,用來區分是否被勾選上,這裡就不再贅述。

3 動手編寫線上調查問卷

  學習完今天的內容之後,我們就可以將它們應用到實際需求中,譬如我們現在需要向其他人發放一份調查問卷,其中涉及到不少輸入文字或單選或多選內容,最後我們還需要將使用者填寫完成的表單內容儲存到本地,用Dash就可以很快速地完成這項工作:

(資料科學學習手札112)Python+Dash快速web應用開發——表單控制元件篇(上)
圖7

  對應的程式碼如下:

app6.py

import dash
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
import json
import re

app = dash.Dash(__name__)

app.layout = html.Div(
    dbc.Container(
        [
            html.H1('關於Dash使用者的調查'),
            html.Br(),

            html.P('1. 您的性別為:'),
            html.Hr(),
            dbc.RadioItems(
                id='gender',
                inline=True,
                options=[
                    {'label': '男', 'value': '男'},
                    {'label': '女', 'value': '女'}
                ]
            ),
            html.Br(),

            html.P('2. 您常用的程式語言有:'),
            html.Hr(),
            dbc.Checklist(
                id='programming-language',
                inline=True,
                options=[
                    {'label': 'Python', 'value': 'Python'},
                    {'label': 'R', 'value': 'R'},
                    {'label': 'JavaScript', 'value': 'JavaScript'},
                    {'label': 'Java', 'value': 'Java'},
                    {'label': 'Julia', 'value': 'Julia'},
                    {'label': 'C#', 'value': 'C#'},
                    {'label': 'C++', 'value': 'C++'},
                    {'label': '其他', 'value': '其他'},
                ]
            ),
            html.Br(),

            html.P('3. 您使用Dash的頻繁程度:'),
            html.Hr(),
            dbc.RadioItems(
                id='frequency',
                inline=True,
                options=[
                    {'label': '經常', 'value': '經常'},
                    {'label': '偶爾', 'value': '偶爾'},
                    {'label': '很少使用', 'value': '很少使用'},
                    {'label': '沒聽說過', 'value': '沒聽說過'},
                ]
            ),
            html.Br(),

            html.P('4. 您對以下哪些方面感興趣:'),
            html.Hr(),
            dbc.Checklist(
                id='interests',
                options=[
                    {'label': '構建線上資料視覺化作品', 'value': '構建線上資料視覺化作品'},
                    {'label': '製作機器學習demo', 'value': '製作機器學習demo'},
                    {'label': '為企業開發BI儀表盤', 'value': '為企業開發BI儀表盤'},
                    {'label': '為企業開發酷炫的指標監控大屏', 'value': '為企業開發酷炫的指標監控大屏'},
                    {'label': '開發有用的線上小工具', 'value': '開發有用的線上小工具'},
                    {'label': '其他', 'value': '其他'},
                ]
            ),
            html.Br(),

            html.P('5. 您的職業:'),
            html.Hr(),
            dbc.RadioItems(
                id='career',
                options=[
                    {'label': '科研人員', 'value': '科研人員'},
                    {'label': '運營', 'value': '運營'},
                    {'label': '資料分析師', 'value': '資料分析師'},
                    {'label': '演算法工程師', 'value': '演算法工程師'},
                    {'label': '大資料開發工程師', 'value': '大資料開發工程師'},
                    {'label': '金融分析師', 'value': '金融分析師'},
                    {'label': '爬蟲工程師', 'value': '爬蟲工程師'},
                    {'label': '學生', 'value': '學生'},
                    {'label': '其他', 'value': '其他'},
                ]
            ),
            html.Br(),

            html.P('您的聯絡方式:'),
            html.Hr(),
            dbc.Input(
                id='tel',
                placeholder='填入您的電話或手機號碼!',
                autoComplete='off', # 關閉瀏覽器自動補全
                style={
                    'width': '300px'
                }
            ),
            html.Hr(),

            dbc.Button(
                '點選提交',
                id='submit'
            ),

            html.P(id='feedback')

        ],
        style={
            'margin-top': '50px',
            'margin-bottom': '200px',
        }
    )
)


@app.callback(
    Output('feedback', 'children'),
    Input('submit', 'n_clicks'),
    [
        State('gender', 'value'),
        State('programming-language', 'value'),
        State('frequency', 'value'),
        State('interests', 'value'),
        State('tel', 'value'),
    ],
    prevent_initial_call=True
)
def fetch_info(n_clicks, gender, programming_language, frequency, interests, tel):
    if all([gender, programming_language, frequency, interests, tel]):

        # 簡單以寫出到本地指定json檔案為例來演示寫出過程
        with open(tel+'.json', 'w') as j:
            json.dump(
                {
                    'gender': gender,
                    'programming_language': programming_language,
                    'frequency': frequency,
                    'interests': interests
                },
                j
            )
        return '提交成功!'

    else:
        return '您的資訊未填寫完整,請檢查後提交!'

@app.callback(
    [Output('tel', 'valid'),
     Output('tel', 'invalid')],
    Input('tel', 'value'),
    prevent_initial_call=True
)
def check_if_tel_completed(value):
    try:
        if re.findall('\d+', value)[0] == value and value.__len__() == 11:
            return True, False
    except:
        pass

    return False, True

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

  以上就是本文的全部內容,歡迎在評論區與我進行討論,分享你的見解~

相關文章