本文示例代碼已上傳至個人
Github
倉庫https://github.com/CNFeffery/DataScienceStudyNotescss
這是個人系列教程Python+Dash快速web應用開發的第十二期,在之前撰寫過的靜態部件篇(中)那期教程中,咱們介紹過在Dash
中建立靜態表格的方法。html
而在實際的使用中,咱們不少時候在網頁中渲染的表格不只僅是爲了對數據進行展現,還須要更多交互能力,譬如按列排序、動態修改表中數值等特性,以及對大型數據表的快速渲染查看能力,諸如此類衆多的交互功能在Dash
自帶的dash_table
中已經實現。git
而接下來的幾期,咱們就將針對如何利用dash_table
建立具備豐富交互功能的表格進行介紹,今天介紹的是dash_table
的基礎使用方法。github
做爲Dash
自帶的拓展庫,咱們經過下列語句導入dash_table
:web
import dash_table
接着像以前使用其餘的Dash
部件同樣,在定義layout
時將dash_table.DataTable()
對象置於咱們定義的合適位置便可,可參考下面的例子配合pandas
的DataFrame
來完成最簡單的表格的渲染。sql
其中參數columns
用於設置每一列對應的名稱與id屬性,data
接受由數據框轉化而成的特殊格式數據,virtualization
設置爲True表明使用了虛擬化技術來加速網頁中大量表格行數據的渲染:數據庫
app1.pybootstrap
import dash import dash_html_components as html import dash_bootstrap_components as dbc import dash_table import seaborn as sns app = dash.Dash(__name__) # 載入演示數據集 df = sns.load_dataset('iris') # 建立行下標列 df.insert(loc=0, column='#', value=df.index) app.layout = html.Div( dbc.Container( dash_table.DataTable( columns=[{'name': column, 'id': column} for column in df.columns], data=df.to_dict('records'), virtualization=True ), style={ 'margin-top': '100px' } ) ) if __name__ == '__main__': app.run_server(debug=True)
若是你對數據的展現徹底沒要求,看個數就行,那上述的這套基礎的參數設置你就能夠當成萬金油來使用,而若是你以爲dash_table.DataTable
默認太醜了(大實話),那麼請繼續閱讀今天的教程。後端
針對DataTable
所渲染出的表格的幾個基礎構成部分,咱們可使用到的用於修改表格樣式的參數有style_table
、style_cell
、style_header
、style_data
等:app
參數style_table
用於對整個表格最外層的容器樣式傳入css鍵值對進行修改,通常用來設定表格的高度、寬度、周圍留白或對齊等屬性:
app2.py
import dash import dash_html_components as html import dash_bootstrap_components as dbc import dash_table import seaborn as sns app = dash.Dash(__name__) # 載入演示數據集 df = sns.load_dataset('iris') # 建立行下標列 df.insert(loc=0, column='#', value=df.index) app.layout = html.Div( dbc.Container( [ dash_table.DataTable( columns=[{'name': column, 'id': column} for column in df.columns], data=df.to_dict('records'), virtualization=True, style_table={ 'height': '200px', 'margin-top': '100px' } ), html.Hr(), dash_table.DataTable( columns=[{'name': column, 'id': column} for column in df.columns], data=df.to_dict('records'), virtualization=True, style_table={ 'height': '200px', 'margin-left': '80px', 'width': '300px' } ), html.Hr(), dash_table.DataTable( columns=[{'name': column, 'id': column} for column in df.columns], data=df.to_dict('records'), virtualization=True, style_table={ 'height': '150px', 'width': '50%', 'margin-left': '50%' } ) ], style={ 'background-color': '#bbdefb' } ) ) if __name__ == '__main__': app.run_server(debug=True)
不一樣於style_table
,使用style_cell
能夠傳入css將樣式應用到全部單元格,而style_header
與style_data
則更加有針對性,可分別對標題單元格、數據單元格進行設置:
app3.py
import dash import dash_html_components as html import dash_bootstrap_components as dbc import dash_table import seaborn as sns app = dash.Dash(__name__) # 載入演示數據集 df = sns.load_dataset('iris') # 建立行下標列 df.insert(loc=0, column='#', value=df.index) app.layout = html.Div( dbc.Container( [ dash_table.DataTable( columns=[{'name': column, 'id': column} for column in df.columns], data=df.to_dict('records'), virtualization=True, style_table={ 'height': '300px' }, style_cell={ 'background-color': '#fff9c4', 'font-family': 'Times New Romer', 'text-align': 'center' } ), html.Hr(), dash_table.DataTable( columns=[{'name': column, 'id': column} for column in df.columns], data=df.to_dict('records'), virtualization=True, style_table={ 'height': '300px' }, style_header={ 'background-color': '#b3e5fc', 'font-family': 'Times New Romer', 'font-weight': 'bold', 'font-size': '17px', 'text-align': 'left' }, style_data={ 'font-family': 'Times New Romer', 'text-align': 'left' } ) ], style={ 'margin-top': '100px' } ) ) if __name__ == '__main__': app.run_server(debug=True)
除了像上文所演示的那樣針對某一類表格構成元素進行總體樣式設置外,DataTable
還爲咱們提供了條件樣式設置,好比咱們想爲特殊的幾列單獨設置樣式,或者爲奇數下標與偶數下標行設置不一樣的樣式,就可使用到這一特性。
這在DataTable
中咱們能夠利用style_header_conditional
與style_data_conditional
來傳入列表,列表中每一個元素均可看作是帶有額外if
鍵值對的css參數字典,而這個if
鍵值對的值亦爲一個字典,其接受的鍵值對種類豐富,咱們今天先來介紹column_id
與row_index
,它們分別用來指定對應id的header
與整行單元格。
參考下面這個例子,咱們分別特殊設置#
列的表頭與奇數行的樣式:
app4.py
import dash import dash_html_components as html import dash_bootstrap_components as dbc import dash_table import seaborn as sns app = dash.Dash(__name__) # 載入演示數據集 df = sns.load_dataset('iris') # 建立行下標列 df.insert(loc=0, column='#', value=df.index) app.layout = html.Div( dbc.Container( [ dash_table.DataTable( columns=[{'name': column, 'id': column} for column in df.columns], data=df.to_dict('records'), virtualization=True, style_table={ 'height': '500px' }, style_cell={ 'font-family': 'Times New Romer', 'text-align': 'center' }, style_header_conditional=[ { 'if': { # 選定列id爲#的列 'column_id': '#' }, 'font-weight': 'bold', 'font-size': '24px' } ], style_data_conditional=[ { 'if': { # 選中行下標爲奇數的行 'row_index': 'odd' }, 'background-color': '#cfd8dc' } ] ) ], style={ 'margin-top': '100px' } ) ) if __name__ == '__main__': app.run_server(debug=True)
設置參數style_as_list_view
爲True能夠隱藏全部豎向的框線,app4
設置以後的效果以下:
學習完今天的內容以後,咱們來動手寫一個簡單的數據入庫應用,經過拖入本地csv
文件以及填寫入庫表名,來實現對上傳數據的預覽與數據庫導入,後端會自動檢查用戶輸入的數據表名稱是否合法,並自動檢測上傳csv
文件的文件編碼。
下面就是該應用工做時的情景,其中由於test
表在庫中已存在,因此會被檢測出不合法:
而當上傳的數據錶行數較多時,右下角會自動出現分頁部件,咱們將在下一期中進行討論,完整代碼以下:
app5.py
import dash import dash_html_components as html import dash_bootstrap_components as dbc from dash.dependencies import Input, Output, State import dash_table import dash_uploader as du import re import os import pandas as pd from sqlalchemy import create_engine import cchardet as chardet # 用於自動識別文件編碼 postgres_url = 'postgresql://postgres:CUDLCUDL@localhost:5432/Dash' engine = create_engine(postgres_url) app = dash.Dash(__name__) du.configure_upload(app, 'upload') app.layout = html.Div( dbc.Container( [ du.Upload( id='upload', filetypes=['csv'], text='點擊或拖動文件到此進行上傳!', text_completed='已完成上傳文件:', cancel_button=True, pause_button=True), html.Hr(), dbc.Form( [ dbc.FormGroup( [ dbc.Label("設置入庫表名", html_for="table-name"), dbc.Input( id='table-name', autoComplete='off' ), dbc.FormText( "表名只容許包含大小寫字母、下劃線或數字,且不能以數字開頭,同時請注意表名是否與庫中現有表重複!", color="secondary" ), dbc.FormFeedback( "表名合法!", valid=True ), dbc.FormFeedback( "表名不合法!", valid=False, ), ] ), dbc.FormGroup( [ dbc.Button('提交入庫', id='commit', outline=True) ] ) ], style={ 'background-color': 'rgba(224, 242, 241, 0.4)' } ), dbc.Spinner( [ html.P(id='commit-status-message', style={'color': 'red'}), dbc.Label('預覽至多前10000行', html_for='uploaded-table'), dash_table.DataTable( id='uploaded-table', style_table={ 'height': '400px' }, virtualization=True, style_as_list_view=True, style_cell={ 'font-family': 'Times New Romer', 'text-align': 'center' }, style_header={ 'font-weight': 'bold' }, style_data_conditional=[ { 'if': { # 選中行下標爲奇數的行 'row_index': 'odd' }, 'background-color': '#cfd8dc' } ] ) ] ) ], style={ 'margin-top': '30px' } ) ) @app.callback( [Output('table-name', 'invalid'), Output('table-name', 'valid')], Input('table-name', 'value') ) def check_table_name(value): '''' 檢查表名是否合法 ''' if value: # 查詢庫中已存在非系統表名 exists_table_names = ( pd .read_sql('''SELECT tablename FROM pg_tables''', con=engine) .query('~(tablename.str.startswith("pg") or tablename.str.startswith("sql_"))') ) if (re.findall('^[A-Za-z0-9_]+$', value)[0].__len__() == value.__len__()) \ and not re.findall('^\d', value) \ and value not in exists_table_names['tablename'].tolist(): return False, True return True, False return dash.no_update @app.callback( Output('commit-status-message', 'children'), Input('commit', 'n_clicks'), [State('table-name', 'valid'), State('table-name', 'value'), State('upload', 'isCompleted'), State('upload', 'fileNames'), State('upload', 'upload_id')] ) def control_table_commit(n_clicks, table_name_valid, table_name, isCompleted, fileNames, upload_id): ''' 控制已上傳表格的入庫 ''' if all([n_clicks, table_name_valid, table_name, isCompleted, fileNames, upload_id]): uploaded_df = pd.read_csv(os.path.join('upload', upload_id, fileNames[0]), encoding=chardet.detect(open(os.path.join('upload', upload_id, fileNames[0]), 'rb').read())['encoding']) uploaded_df.to_sql(table_name, con=engine) return '入庫成功!' return dash.no_update @app.callback( [Output('uploaded-table', 'data'), Output('uploaded-table', 'columns')], Input('upload', 'isCompleted'), [State('upload', 'fileNames'), State('upload', 'upload_id')] ) def render_table(isCompleted, fileNames, upload_id): ''' 控制預覽表格的渲染 ''' if isCompleted: uploaded_df = pd.read_csv(os.path.join('upload', upload_id, fileNames[0]), encoding=chardet.detect(open(os.path.join('upload', upload_id, fileNames[0]), 'rb').read())['encoding']).head(10000) uploaded_df.insert(0, '#', range(uploaded_df.shape[0])) return uploaded_df.to_dict('record'), [{'name': column, 'id': column} for column in uploaded_df.columns] return dash.no_update if __name__ == '__main__': app.run_server(debug=True)
以上就是本文的所有內容,歡迎在評論區與我進行討論~