本文示例代码已上传至个人
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)
以上就是本文的所有内容,欢迎在评论区与我进行讨论~