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

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

> 本文示例程式碼已上傳至我的`Github`倉庫[https://github.com/CNFeffery/DataScienceStudyNotes](https://github.com/CNFeffery/DataScienceStudyNotes) # 1 簡介    這是我的系列教程**Python+Dash快速web應用開發**的第九期,在之前三期的教程中,我們針對`Dash`中經常會用到的一些靜態部件進行了較為詳細的介紹,從而get到在`Dash`應用中組織靜態內容的常用方法。   而從今天的教程開始,我將帶大家來認識和學習`Dash`生態中非常實用的一些**互動式**部件,配合回撥函式,可以幫助我們構建一個形式豐富的可接受輸入,並反饋輸出的互動式應用,今天要介紹的互動部件為**表單輸入**類部件的基礎知識,下面來學習吧~
圖1
# 2 Dash中常用的表單輸入類互動部件   **互動部件**跟之前介紹的一系列**靜態部件**的區別在於它們不僅具有供使用者互動操作的特點,還承擔了接受使用者輸入,並傳遞這些輸入引數的作用。而網頁開發中,**表單輸入**類部件則是互動部件中最常用到的。   在`Dash`生態中常用到的表單輸入類互動部件有: ## 2.1 輸入框部件Input()   其實在之前的教程內容中我們已經使用過很多次輸入框部件`Input()`了,而我比較推薦使用的是`dash_bootstrap_components`中封裝的`Input()`,它相較於`dash_core_components`中自帶的`Input()`擁有更多特性。   除了幾乎所有部件都具有的`id`、`className`以及`style`引數之外,`Input()`中還有一個特殊的引數`type`,它的不同取值從根本上奠定了`Input()`的角色,常用的有: - **text、password、search**   當`Input()`的`type`引數取值為`'text'`、`'password'`以及`'search'`之一時,它分別扮演文字輸入框、密碼輸入框以及搜尋框等角色,也擁有了一些特別的常用引數&屬性:   `value`屬性對應它當前的輸入值;   `placeholder`用於設定未輸入時輸入框內的提示文字;   `maxLength`用於設定最多可輸入的字元數量;   `n_submit`用於記錄游標在輸入框內部時鍵盤`Enter`鍵被點按的次數;   `debounce`設定為`True`時會強制每次使用者按下`Enter`鍵或點選其他部件時才同步`value`值給後臺`Dash`服務。   `valid`和`invalid`引數都接受Bool型引數,分別用來控制輸入框顯示正確狀態以及錯誤狀態,我們可以在檢查使用者名稱、密碼等是否正確時通過回撥輸出設定這些引數為True來告知使用者相關提示資訊。   我們來通過下面的示例來直觀感受這些特性: >
app1.py ```Python 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) ```
圖2
- **number、range**   當`Input()`部件的`type`屬性設定為`'number'`時,它便搖身一變成了數值輸入框,並擁有了一些特殊的引數&屬性:   `min`與`max`引數用來約束數值輸入框的輸入值上下限;   `step`引數用來設定數值輸入框右側上下箭頭點按一次後數值變化的步長   而當`type`設定為`range`時就更有意思了,我們的`Input()`這時變成了一個滑桿,也是通過上述三個引數來限制範圍和拖動的步長值。 > app2.py ```Python 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) ```
圖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 ```Python 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) ```
圖4
## 2.3 單選框與複選框   我們分別可以使用`dash_bootstrap_components`中的`RadioItems`與`Checklist`來建立單選框與複選框: - **單選框RadioItems**   單選框的特點是我們只能在其展示的一組選項中選擇1項。   它的引數`options`格式同`Dropdown()`;   `inline`引數設定為True時會橫向佈局所有選項;   `switch`設定為True時會將每個選項樣式切換為開關; > app4.py ```Python 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) ```
圖5
- **複選框Checklist**   與單選框相對的,是複選框,它的引數與`RadioItems`完全一致,唯一不同的是它是可以多選的: > app5.py ```Python 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) ```
圖6
  而除了上述兩種供使用者對多個選項進行單選或多選的部件之外,`dash_bootstrap_components`中還有可以建立單個選擇部件的`RadioButton`與`Checkbox`,它們只能進行勾選操作,對應回撥用的的輸入值為`checked`,是個Bool型屬性,用來區分是否被勾選上,這裡就不再贅述。 # 3 動手編寫線上調查問卷   學習完今天的內容之後,我們就可以將它們應用到實際需求中,譬如我們現在需要向其他人發放一份調查問卷,其中涉及到不少輸入文字或單選或多選內容,最後我們還需要將使用者填寫完成的表單內容儲存到本地,用`Dash`就可以很快速地完成這項工作:
圖7
  對應的程式碼如下: > app6.py ```Python 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) ``` ---   以上就是本文的全部內容,歡迎在評論區與我進行討論,分享你