用 Python 制作酷炫的可视化大屏,特简单!

开发 后端
以前给大家介绍过使用Streamlit库制作大屏,今天给大家带来一个新方法。

 在数据时代,我们每个人既是数据的生产者,也是数据的使用者,然而初次获取和存储的原始数据杂乱无章、信息冗余、价值较低。

要想数据达到生动有趣、让人一目了然、豁然开朗的效果,就需要借助数据可视化。

以前给大家介绍过使用Streamlit库制作大屏,今天给大家带来一个新方法。

通过Python的Dash库,来制作一个酷炫的可视化大屏!

先来看一下整体效果,好像还不错哦。

主要使用Python的Dash库、Plotly库、Requests库。

其中Requests爬取数据,Plotly制作可视化图表,Dash搭建可视化页面。

原始数据是小F的博客数据,数据存储在MySqL数据库中。

如此看来,和Streamlit库的搭建流程,所差不多。

关于Dash库,网上的资料不是很多,基本上只能看官方文档和案例,下面小F简单介绍一下。

Dash是一个用于构建Web应用程序的高效Python框架,特别适合使用Python进行数据分析的人。

Dash是建立在Flask,Plotly.js和React.js之上,非常适合在纯Python中,使用高度自定义的用户界面,构建数据可视化应用程序。

相关文档

说明:https://dash.plotly.com/introduction

案例:https://dash.gallery/Portal/

源码:https://github.com/plotly/dash-sample-apps/

具体的大家可以去看文档学习,多动手练习。

下面就给大家讲解下如何通过Dash搭建可视化大屏~

01. 数据

使用的数据是博客数据,主要是下方两处红框的信息。

通过爬虫代码爬取下来,存储在MySQL数据库中。

其中MySQL的安装,大家可以自行百度,都挺简单的。

安装好后,进行启用,以及创建数据库。 

  1. # 启动MySQL, 输入密码  
  2. mysql -u root -p  
  3. # 创建名为my_database的数据库  
  4. create database my_database; 

其它相关的操作命令如下所示。 

  1. # 显示MySQL中所有的数据库  
  2. show databases;  
  3. # 选择my_database数据库  
  4. use my_database;  
  5. # 显示my_database数据库中所有的表  
  6. show tables;  
  7. # 删除表  
  8. drop table info;  
  9. drop table `2021-12-26`;  
  10. # 显示表中的内容, 执行SQL查询语句  
  11. select * from info;  
  12. select * from `2021-12-26`; 

搞定上面的步骤后,就可以运行爬虫代码。

数据爬取代码如下。这里使用到了pymysql这个库,需要pip安装下。 

  1. import requests  
  2. import re 
  3. from bs4 import BeautifulSoup  
  4. import time  
  5. import random  
  6. import pandas as pd  
  7. from sqlalchemy import create_engine 
  8. import datetime as dt  
  9. def get_info():  
  10.     """获取大屏第一列信息数据"""  
  11.     headers = {  
  12.         'User-Agent': 'Mozilla/5.0 (MSIE 10.0; Windows NT 6.1; Trident/5.0)',  
  13.         'referer': 'https: // passport.csdn.net / login',  
  14.     }  
  15.     # 我的博客地址  
  16.     url = 'https://blog.csdn.net/river_star1/article/details/121463591'  
  17.     try:  
  18.         resp = requests.get(url, headersheaders=headers)  
  19.         now = dt.datetime.now().strftime("%Y-%m-%d %X")  
  20.         soup = BeautifulSoup(resp.text, 'lxml')  
  21.         author_name = soup.find('div', class_='user-info d-flex flex-column profile-intro-name-box').find('a').get_text(strip=True 
  22.         head_img = soup.find('div', class_='avatar-box d-flex justify-content-center flex-column').find('a').find('img')['src']  
  23.         row1_nums = soup.find_all('div', class_='data-info d-flex item-tiling')[0].find_all('span', class_='count' 
  24.         row2_nums = soup.find_all('div', class_='data-info d-flex item-tiling')[1].find_all('span', class_='count' 
  25.         level_mes = soup.find_all('div', class_='data-info d-flex item-tiling')[0].find_all('dl')[-1]['title'].split(',')[0]  
  26.         rank = soup.find('div', class_='data-info d-flex item-tiling').find_all('dl')[-1]['title']  
  27.         info = {  
  28.             'date': now,#时间  
  29.             'head_img': head_img,#头像  
  30.             'author_name': author_name,#用户名  
  31.             'article_num': str(row1_nums[0].get_text()),#文章数  
  32.             'fans_num': str(row2_nums[1].get_text()),#粉丝数  
  33.             'like_num': str(row2_nums[2].get_text()),#喜欢数  
  34.             'comment_num': str(row2_nums[3].get_text()),#评论数  
  35.             'level': level_mes,#等级  
  36.             'visit_num': str(row1_nums[3].get_text()),#访问数  
  37.             'score': str(row2_nums[0].get_text()),#积分  
  38.             'rank': str(row1_nums[2].get_text()),#排名  
  39.         }  
  40.         df_info = pd.DataFrame([info.values()], columns=info.keys())  
  41.         return df_info  
  42.     except Exception as e:  
  43.         print(e)  
  44.         return get_info()  
  45. def get_type(title):  
  46.     """设置文章类型(依据文章名称)"""  
  47.     the_type = '其他'  
  48.     article_types = ['项目', '数据可视化', '代码', '图表', 'Python', '可视化', '数据', '面试', '视频', '动态', '下载']  
  49.     for article_type in article_types:  
  50.         if article_type in title:  
  51.             the_type = article_type  
  52.             break  
  53.     return the_type  
  54. def get_blog():  
  55.     """获取大屏第二、三列信息数据"""  
  56.     headers = {  
  57.         'User-Agent': 'Mozilla/5.0 (MSIE 10.0; Windows NT 6.1; Trident/5.0)', 
  58.         'referer': 'https: // passport.csdn.net / login',  
  59.     }  
  60.     base_url = 'https://blog.csdn.net/river_star1/article/list/'  
  61.     resp = requests.get(base_url+"1", headersheaders=headers,  timeout=3 
  62.     max_page = int(re.findall(r'var listTotal = (\d+);', resp.text)[0])//40+1  
  63.     df = pd.DataFrame(columns=['url', 'title', 'date', 'read_num', 'comment_num', 'type'])  
  64.     count = 0  
  65.     for i in range(1, max_page+1):  
  66.         url = base_url + str(i)  
  67.         resp = requests.get(url, headersheaders=headers)  
  68.         soup = BeautifulSoup(resp.text, 'lxml')  
  69.         articles = soup.find("div", class_='article-list').find_all('div', class_='article-item-box csdn-tracking-statistics' 
  70.         for article in articles[1:]:  
  71.             a_url = article.find('h4').find('a')['href']  
  72.             title = article.find('h4').find('a').get_text(strip=True)[2:]  
  73.             issuing_time = article.find('span', class_="date").get_text(strip=True 
  74.             num_list = article.find_all('span', class_="read-num" 
  75.             read_num = num_list[0].get_text(strip=True 
  76.             if len(num_list) > 1:  
  77.                 comment_num = num_list[1].get_text(strip=True 
  78.             else:  
  79.                 comment_num = 0  
  80.             the_type = get_type(title)  
  81.             df.loc[count] = [a_url, title, issuing_time, int(read_num), int(comment_num), the_type]  
  82.             count += 1  
  83.         time.sleep(random.choice([1, 1.1, 1.3]))  
  84.     return df  
  85. if __name__ == '__main__':  
  86.     # 今天的时间  
  87.     today = dt.datetime.today().strftime("%Y-%m-%d")  
  88.     # 连接mysql数据库  
  89.     engine = create_engine('mysql+pymysql://root:123456@localhost/my_database?charset=utf8')  
  90.     # 获取大屏第一列信息数据, 并写入my_database数据库的info表中, 如若表已存在, 删除覆盖  
  91.     df_info = get_info()  
  92.     print(df_info)  
  93.     df_info.to_sql("info", con=engineif_exists='replace'index=False 
  94.     # 获取大屏第二、三列信息数据, 并写入my_database数据库的日期表中, 如若表已存在, 删除覆盖  
  95.     df_article = get_blog()  
  96.     print(df_article)  
  97.     df_article.to_sql(today, con=engineif_exists='replace'index=True

运行成功后,就可以去数据库查询信息了。

info表,包含日期、头图、博客名、文章数、粉丝数、点赞数、评论数、等级数、访问数、积分数、排名数。

日期表,包含文章地址、标题、日期、阅读数、评论数、类型。

其中爬虫代码可设置定时运行,info表为60秒,日期表为60分钟。

尽量不要太频繁,容易被封IP,或者选择使用代理池。

这样便可以做到数据实时更新。

既然数据已经有了,下面就可以来编写页面了。

02. 大屏搭建

导入相关的Python库,同样可以通过pip进行安装。 

  1. from spider_py import get_info, get_blog  
  2. from dash import dcc  
  3. import dash  
  4. from dash import html  
  5. import pandas as pd  
  6. import plotly.graph_objs as go  
  7. from dash.dependencies import Input, Output  
  8. import datetime as dt  
  9. from sqlalchemy import create_engine  
  10. from flask_caching import Cache 
  11.  import numpy as np 

设置一些基本的配置参数,如数据库连接、网页样式、Dash实例、图表颜色。 

  1. # 今天的时间  
  2. today = dt.datetime.today().strftime("%Y-%m-%d")  
  3. # 连接数据库  
  4. engine = create_engine('mysql+pymysql://root:123456@localhost/my_database?charset=utf8 
  5. # 导入css样式  
  6. external_css = [  
  7.     "https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css",  
  8.     "https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css"  
  9.  
  10. # 创建一个实例  
  11. app = dash.Dash(__name__, external_stylesheets=external_css 
  12. server = app.server  
  13. # 可以选择使用缓存, 减少频繁的数据请求  
  14. cache = Cache(app.server, config={  
  15. #     'CACHE_TYPE': 'filesystem',  
  16. #     'CACHE_DIR': 'cache-directory'  
  17. # })  
  18. # 读取info表的数据  
  19. info = pd.read_sql('info', con=engine 
  20. # 图表颜色  
  21. color_scale = ['#2c0772', '#3d208e', '#8D7DFF', '#CDCCFF', '#C7FFFB', '#ff2c6d', '#564b43', '#161d33'] 

这里将缓存代码注释掉了,如有频繁的页面刷新请求,就可以选择使用。 

  1. def indicator(text, id_value):  
  2.     """第一列的文字及数字信息显示"""  
  3.     return html.Div([  
  4.     html.P(text, className="twelve columns indicator_text"),  
  5.     html.P(id=id_valueclassName="indicator_value"),  
  6. ], className="col indicator" 
  7. def get_news_table(data):  
  8.     """获取文章列表, 根据阅读排序"""  
  9.     df = data.copy()  
  10.     df.sort_values('read_num', inplace=Trueascending=False 
  11.     titles = df['title'].tolist()  
  12.     urls = df['url'].tolist()  
  13.     return html.Table([html.Tbody([  
  14.         html.Tr([  
  15.             html.Td(  
  16.                 html.A(titles[i], href=urls[i], target="_blank",))  
  17.         ], style={'height': '30px', 'fontSize': '16'})for i in range(min(len(df), 100))  
  18.     ])], style={"height": "90%", "width": "98%"})  
  19. # @cache.memoize(timeout=3590), 可选择设置缓存, 我没使用  
  20. def get_df():  
  21.     """获取当日最新的文章数据"""  
  22.     df = pd.read_sql(today, con=engine 
  23.     df['date_day'] = df['date'].apply(lambda x: x.split(' ')[0]).astype('datetime64[ns]')  
  24.     df['date_month'] = df['date'].apply(lambda x: x[:7].split('-')[0] + "年" + x[:7].split('-')[-1] + "月")  
  25.     df['weekday'] = df['date_day'].dt.weekday  
  26.     df['year'] = df['date_day'].dt.year  
  27.     df['month'] = df['date_day'].dt.month  
  28.     df['week'] = df['date_day'].dt.isocalendar().week  
  29.     return df  
  30. # 导航栏的图片及标题  
  31. head = html.Div([  
  32.     html.Div(html.Img(src='./assets/img.jpg'height="100%"), style={"float": "left", "height": "90%", "margin-top": "5px", "border-radius": "50%", "overflow": "hidden"}), 
  33.     html.Span("{}博客的Dashboard".format(info['author_name'][0]), className='app-title'),  
  34. ], className="row header" 
  35. # 第一列的文字及数字信息  
  36. columns = info.columns[3:]  
  37. col_name = ['文章数', '关注数', '喜欢数', '评论数', '等级', '访问数', '积分', '排名']  
  38. row1 = html.Div([  
  39.     indicator(col_name[i], col) for i, col in enumerate(columns)  
  40. ], className='row' 
  41. # 第二列  
  42. row2 = html.Div([  
  43.     html.Div([  
  44.         html.P("每月文章写作情况"),  
  45.         dcc.Graph(id="bar"style={"height": "90%", "width": "98%"}, config=dict(displayModeBar=False),)  
  46.     ], className="col-4 chart_div",),  
  47.     html.Div([  
  48.         html.P("各类型文章占比情况"),  
  49.         dcc.Graph(id="pie"style={"height": "90%", "width": "98%"}, config=dict(displayModeBar=False),) 
  50.     ], className="col-4 chart_div"),  
  51.     html.Div([  
  52.         html.P("各类型文章阅读情况"),  
  53.         dcc.Graph(id="mix"style={"height": "90%", "width": "98%"}, config=dict(displayModeBar=False),)  
  54.     ], className="col-4 chart_div",)  
  55. ], className='row' 
  56. # 年数统计, 我的是2019 2020 2021  
  57. years = get_df()['year'].unique()  
  58. select_list = ['每月文章', '类型占比', '类型阅读量', '每日情况']  
  59. # 两个可交互的下拉选项  
  60. dropDowm1 = html.Div([  
  61.     html.Div([  
  62.         dcc.Dropdown(id='dropdown1' 
  63.                  options=[{'label': '{}年'.format(year), 'value': year} for year in years],  
  64.                  value=years[1], style={'width': '40%'})  
  65.         ], className='col-6'style={'padding': '2px', 'margin': '0px 5px 0px'}),  
  66.     html.Div([  
  67.         dcc.Dropdown(id='dropdown2' 
  68.                  options=[{'label': select_list[i], 'value': item} for i, item in enumerate(['bar', 'pie', 'mix', 'heatmap'])],  
  69.                  value='heatmap'style={'width': '40%'})  
  70.         ], className='col-6'style={'padding': '2px', 'margin': '0px 5px 0px'})  
  71. ], className='row' 
  72. # 第三列  
  73. row3 = html.Div([  
  74.     html.Div([  
  75.         html.P("每日写作情况"),  
  76.         dcc.Graph(id="heatmap"style={"height": "90%", "width": "98%"}, config=dict(displayModeBar=False),)  
  77.     ], className="col-6 chart_div",),  
  78.     html.Div([  
  79.         html.P("文章列表"),  
  80.         html.Div(get_news_table(get_df()), id='click-data'),  
  81.     ], className="col-6 chart_div"style={"overflowY": "scroll"})  
  82. ], className='row' 
  83. # 总体情况  
  84. app.layout = html.Div([  
  85.     # 定时器  
  86.     dcc.Interval(id="stream"interval=1000*60, n_intervals=0),  
  87.     dcc.Interval(id="river"interval=1000*60*60, n_intervals=0),  
  88.     html.Div(id="load_info"style={"display": "none"},),  
  89.     html.Div(id="load_click_data"style={"display": "none"},),  
  90.     head,  
  91.     html.Div([  
  92.         row1,  
  93.         row2,  
  94.         dropDowm1,  
  95.         row3,  
  96.     ], style={'margin': '0% 30px'}),  
  97. ]) 

上面的代码,就是网页的布局,效果如下。

网页可以划分为三列。第一列为info表中的数据展示,第二、三列为博客文章的数据展示。

相关的数据需要通过回调函数进行更新,这样才能做到实时刷新。

各个数值及图表的回调函数代码如下所示。 

  1. # 回调函数, 60秒刷新info数据, 即第一列的数值实时刷新  
  2. @app.callback(Output('load_info', 'children'), [Input("stream", "n_intervals")])  
  3. def load_info(n):  
  4.     try:  
  5.         df = pd.read_sql('info', con=engine 
  6.         return df.to_json()  
  7.     except:  
  8.         pass  
  9. # 回调函数, 60分钟刷新今日数据, 即第二、三列的数值实时刷新(爬取文章数据, 并写入数据库中)  
  10. @app.callback(Output('load_click_data', 'children'), [Input("river", "n_intervals")])  
  11. def cwarl_data(n):  
  12.     if n != 0:  
  13.         df_article = get_blog()  
  14.         df_article.to_sql(today, con=engineif_exists='replace'index=True 
  15. # 回调函数, 第一个柱状图  
  16. @app.callback(Output('bar', 'figure'), [Input("river", "n_intervals")])  
  17. def get_bar(n):  
  18.     df = get_df()  
  19.     df_date_month = pd.DataFrame(df['date_month'].value_counts(sort=False))  
  20.     df_date_month.sort_index(inplace=True 
  21.     trace = go.Bar(  
  22.         x=df_date_month.index,  
  23.         y=df_date_month['date_month'],  
  24.         text=df_date_month['date_month'],  
  25.         textposition='auto' 
  26.         marker=dict(color='#33ffe6' 
  27.     )  
  28.     layout = go.Layout(  
  29.         margin=dict(l=40r=40t=10b=50),  
  30.         yaxis=dict(gridcolor='#e2e2e2'),  
  31.         paper_bgcolor='rgba(0,0,0,0)' 
  32.         plot_bgcolor='rgba(0,0,0,0)' 
  33.     )  
  34.     return go.Figure(data=[trace], layoutlayout=layout)  
  35. # 回调函数, 中间的饼图  
  36. @app.callback(Output('pie', 'figure'), [Input("river", "n_intervals")])  
  37. def get_pie(n):  
  38.     df = get_df()  
  39.     df_types = pd.DataFrame(df['type'].value_counts(sort=False))  
  40.     trace = go.Pie(  
  41.         labels=df_types.index,  
  42.         values=df_types['type'],  
  43.         marker=dict(colors=color_scale[:len(df_types.index)])  
  44.     ) 
  45.    layout = go.Layout(  
  46.         margin=dict(l=50r=50t=50b=50),  
  47.         paper_bgcolor='rgba(0,0,0,0)' 
  48.         plot_bgcolor='rgba(0,0,0,0)' 
  49.     )  
  50.     return go.Figure(data=[trace], layoutlayout=layout)  
  51. # 回调函数, 左下角热力图  
  52. @app.callback(Output('heatmap', 'figure'),  
  53.               [Input("dropdown1", "value"), Input('river', 'n_intervals')])  
  54. def get_heatmap(value, n):  
  55.     df = get_df()  
  56.     grouped_by_year = df.groupby('year') 
  57.     data = grouped_by_year.get_group(value)  
  58.     cross = pd.crosstab(data['weekday'], data['week'])  
  59.     cross.sort_index(inplace=True 
  60.     trace = go.Heatmap(  
  61.         x=['第{}周'.format(i) for i in cross.columns],  
  62.         y=["星期{}".format(i+1) if i != 6 else "星期日" for i in cross.index],  
  63.         z=cross.values, 
  64.        colorscale="Blues" 
  65.         reversescale=False 
  66.         xgap=4 
  67.         ygap=5 
  68.         showscale=False  
  69.     )  
  70.     layout = go.Layout(  
  71.         margin=dict(l=50r=40t=30b=50),  
  72.     )  
  73.     return go.Figure(data=[trace], layoutlayout=layout)  
  74. # 回调函数, 第二个柱状图(柱状图+折线图)  
  75. @app.callback(Output('mix', 'figure'), [Input("river", "n_intervals")])  
  76. def get_mix(n):  
  77.     df = get_df()  
  78.     df_type_visit_sum = pd.DataFrame(df['read_num'].groupby(df['type']).sum())  
  79.     df['read_num'] = df['read_num'].astype('float')  
  80.     df_type_visit_mean = pd.DataFrame(df['read_num'].groupby(df['type']).agg('mean').round(2))  
  81.     trace1 = go.Bar(  
  82.         x=df_type_visit_sum.index,  
  83.         y=df_type_visit_sum['read_num'],  
  84.         name='总阅读' 
  85.         marker=dict(color='#ffc97b'),  
  86.         yaxis='y' 
  87.     )  
  88.     trace2 = go.Scatter(  
  89.         x=df_type_visit_mean.index,  
  90.         y=df_type_visit_mean['read_num'],  
  91.         name='平均阅读' 
  92.         yaxis='y2' 
  93.         line=dict(color='#161D33' 
  94.     )  
  95.     layout = go.Layout(  
  96.         margin=dict(l=60r=60t=30b=50),  
  97.         showlegend=False 
  98.         yaxis=dict 
  99.             side='left' 
  100.             title='阅读总数' 
  101.             gridcolor='#e2e2e2'  
  102.         ),  
  103.         yaxis2=dict 
  104.             showgrid=False,  # 网格  
  105.             title='阅读平均' 
  106.             anchor='x' 
  107.             overlaying='y' 
  108.             side='right'  
  109.         ),  
  110.         paper_bgcolor='rgba(0,0,0,0)' 
  111.         plot_bgcolor='rgba(0,0,0,0)' 
  112.     )  
  113.     return go.Figure(data=[trace1, trace2], layoutlayout=layout)  
  114. # 点击事件, 选择两个下拉选项, 点击对应区域的图表, 文章列表会刷新  
  115. @app.callback(Output('click-data', 'children'),  
  116.         [Input('pie', 'clickData'),  
  117.          Input('bar', 'clickData'),  
  118.          Input('mix', 'clickData'),  
  119.          Input('heatmap', 'clickData'),  
  120.          Input('dropdown1', 'value'),  
  121.          Input('dropdown2', 'value'),  
  122.          ])  
  123. def display_click_data(pie, bar, mix, heatmap, d_value, fig_type): 
  124.     try:  
  125.         df = get_df()  
  126.         if fig_type == 'pie':  
  127.             type_value = pie['points'][0]['label']  
  128.             # date_month_value = clickdata['points'][0]['x']  
  129.             data = df[df['type'] == type_value]  
  130.         elif fig_type == 'bar':  
  131.             date_month_value = bar['points'][0]['x']  
  132.             data = df[df['date_month'] == date_month_value]  
  133.         elif fig_type == 'mix':  
  134.             type_value = mix['points'][0]['x']  
  135.             data = df[df['type'] == type_value] 
  136.         else:  
  137.             z = heatmap['points'][0]['z']  
  138.             if z == 0:  
  139.                 return None  
  140.             else:  
  141.                 week = heatmap['points'][0]['x'][1:-1]  
  142.                 weekday = heatmap['points'][0]['y'][-1]  
  143.                 if weekday == '日':  
  144.                     weekday = 7  
  145.                 year = d_value  
  146.                 data = df[(df['weekday'] == int(weekday)-1) & (df['week'] == int(week)) & (df['year'] == year)]  
  147.         return get_news_table(data)  
  148.     except:  
  149.         return None  
  150. # 第一列的数值  
  151. def update_info(col):  
  152.     def get_data(json, n):  
  153.         df = pd.read_json(json)  
  154.         return df[col][0]  
  155.     return get_data  
  156. for col in columns:  
  157.     app.callback(Output(col, "children"),  
  158.                  [Input('load_info', 'children'), Input("stream", "n_intervals")]  
  159.      )(update_info(col)) 

图表的数据和样式全在这里设置,两个下拉栏的数据交互也在这里完成。

需要注意右侧下拉栏的类型,需和你所要点击图表类型一致,这样文章列表才会更新。

每日情况对应热力图,类型阅读量对应第二列第三个图表,类型占比对应饼图,每月文章对应第一个柱状图的点击事件。

最后启动程序代码。 

  1. if __name__ == '__main__':  
  2.     # debug模式, 端口7777  
  3.     app.run_server(debug=Truethreaded=Trueport=7777 
  4.     # 正常模式, 网页右下角的调试按钮将不会出现  
  5.     # app.run_server(port=7777

这样就能在本地看到可视化大屏页面,浏览器打开如下地址。

http://127.0.0.1:7777

对于网页的布局、背景颜色等,主要通过CSS进行设置。

这一部分可能是大家所要花费时间去理解的。 

  1. body{  
  2.     margin:0;  
  3.     padding: 0;  
  4.     background-color: #161D33;  
  5.     font-family: 'Open Sans', sans-serif;  
  6.     color: #506784;  
  7.     -webkit-user-select: none;  /* Chrome all / Safari all */  
  8.     -moz-user-select: none;     /* Firefox all */  
  9.     -ms-user-select: none;      /* IE 10+ */  
  10.     user-select: none;          /* Likely future */  
  11. .modal {  
  12.     display: block;  /*Hidden by default */  
  13.     position: fixed; /* Stay in place */  
  14.     z-index: 1000; /* Sit on top */  
  15.     left: 0;  
  16.     top: 0;  
  17.     width: 100%; /* Full width */  
  18.     height: 100%; /* Full height */  
  19.     overflow: auto; /* Enable scroll if needed */  
  20.     background-color: rgb(0,0,0); /* Fallback color */  
  21.     background-color: rgba(0,0,0,0.4); /* Black w/ opacity */  
  22.  
  23. .modal-content {  
  24.     background-color: white;  
  25.     margin: 5% auto; /* 15% from the top and centered */  
  26.     padding: 20px;  
  27.     width: 30%; /* Could be more or less, depending on screen size */  
  28.     color:#506784;  
  29.  
  30. ._dash-undo-redo {  
  31.   display: none;  
  32. .app-title{  
  33.     color:white;  
  34.     font-size:3rem;  
  35.     letter-spacing:-.1rem; 
  36.     padding:10px;  
  37.     vertical-align:middle  
  38.  
  39. .header{  
  40.     margin:0px;  
  41.     background-color:#161D33;  
  42.     height:70px;  
  43.     color:white;  
  44.     padding-right:2%;  
  45.     padding-left:2%  
  46.  
  47. .indicator{  
  48.   border-radius: 5px;  
  49.   background-color: #f9f9f9;  
  50.   margin: 10px;  
  51.   padding: 15px;  
  52.   position: relative;  
  53.   box-shadow: 2px 2px 2px lightgrey;  
  54.  
  55. .indicator_text{  
  56.     text-align: center;  
  57.     float: left;  
  58.     font-size: 17px;  
  59.     }  
  60. .indicator_value{  
  61.     text-align:center;  
  62.     color: #2a3f5f;  
  63.     font-size: 35px;  
  64.  
  65. .add{  
  66.     height: 34px;  
  67.     background: #119DFF;  
  68.     border: 1px solid #119DFF;  
  69.     color: white;  
  70.  
  71. .chart_div{  
  72.     background-color: #f9f9f9;  
  73.     border-radius: 5px;  
  74.     height: 390px;  
  75.     margin:5px;  
  76.     padding: 15px;  
  77.     position: relative;  
  78.     box-shadow: 2px 2px 2px lightgrey;  
  79. .col-4 {  
  80.     flex: 0 0 32.65%;  
  81.     max-width: 33%;  
  82.  
  83. .col-6 {  
  84.     flex: 0 0 49.3%;  
  85.     max-width: 50%;  
  86.  
  87. .chart_div p{  
  88.     color: #2a3f5f;  
  89.     font-size: 15px;  
  90.     text-align: center;  
  91.  
  92. td{  
  93.     text-align: left;  
  94.     padding: 0px;  
  95.  
  96. table{  
  97.     border: 1px;  
  98.     font-size:1.3rem;  
  99.     width:100%;  
  100.     font-family:Ubuntu;  
  101.  
  102. .tabs_div{  
  103.     margin:0px;  
  104.     height:30px;  
  105.     font-size:13px; 
  106.     margin-top:1px  
  107.  
  108. tr:nth-child(even) {  
  109.     background-color: #d6e4ea;  
  110.     -webkit-print-color-adjust: exact;  

如今低代码平台的出现,或许以后再也不用去写烦人的HTML、CSS等。拖拖拽拽,即可轻松完成一个大屏的制作。

好了,今天的分享到此结束,大家可以自行去动手练习。 

 

责任编辑:庞桂玉 来源: AI科技大本营
相关推荐

2022-08-17 09:01:16

数据可视化大数据

2019-07-26 09:19:32

数据可视化架构

2022-09-29 11:16:21

Python数据可视化

2021-03-09 08:32:50

开发视化大屏H5-Dooring

2023-09-26 08:01:16

2022-02-28 08:34:42

开发可视化大屏

2022-04-13 09:01:53

Echart5绘制地图

2020-09-07 13:02:22

地球Python代码

2021-04-19 09:00:54

Python批量下载视频下载器

2023-12-27 10:47:45

Flask数据可视化开发

2024-03-11 08:32:02

2019-05-20 08:20:40

数据集数据可视化数据

2023-10-12 08:02:36

2021-09-26 16:20:04

Sentry Dashboards 数据可视化

2023-03-19 22:51:11

可视化项目可视化图表

2021-04-14 16:20:39

可视化大数据工具

2018-03-21 12:13:47

工具数据开发

2021-07-12 17:23:47

零设计可视化引擎

2020-09-18 16:37:59

数据可视化技术Python

2024-02-26 12:02:37

Python数据可视化D3blocks
点赞
收藏

51CTO技术栈公众号