{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# `dcc.Tabs`\n", "\n", "参考:[tabs](https://dash.plotly.com/dash-core-components/tabs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`dcc.Tabs` 和 `dcc.Tab` 组件可以用来在应用中创建选项卡式区域。`dcc.Tab` 组件控制单个选项卡的样式和值,而 `dcc.Tabs` 组件则包含一组 `dcc.Tab` 组件。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 方法 1. 内容作为回调\n", "\n", "将回调附加到 Tabs 的 `value` 属性,并在回调中更新容器的 `children` 属性。" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from dash import Dash, dcc, html, Input, Output, callback\n", "\n", "external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']\n", "\n", "app = Dash(__name__, external_stylesheets=external_stylesheets)\n", "\n", "app.layout = html.Div([\n", " html.H1('Dash Tabs component demo'),\n", " dcc.Tabs(id=\"tabs-example-graph\", value='tab-1-example-graph', children=[\n", " dcc.Tab(label='Tab One', value='tab-1-example-graph'),\n", " dcc.Tab(label='Tab Two', value='tab-2-example-graph'),\n", " ]),\n", " html.Div(id='tabs-content-example-graph')\n", "])\n", "\n", "@callback(Output('tabs-content-example-graph', 'children'),\n", " Input('tabs-example-graph', 'value'))\n", "def render_content(tab):\n", " if tab == 'tab-1-example-graph':\n", " return html.Div([\n", " html.H3('Tab content 1'),\n", " dcc.Graph(\n", " figure={\n", " 'data': [{\n", " 'x': [1, 2, 3],\n", " 'y': [3, 1, 2],\n", " 'type': 'bar'\n", " }]\n", " }\n", " )\n", " ])\n", " elif tab == 'tab-2-example-graph':\n", " return html.Div([\n", " html.H3('Tab content 2'),\n", " dcc.Graph(\n", " id='graph-2-tabs-dcc',\n", " figure={\n", " 'data': [{\n", " 'x': [1, 2, 3],\n", " 'y': [5, 10, 6],\n", " 'type': 'bar'\n", " }]\n", " }\n", " )\n", " ])\n", "\n", "# if __name__ == '__main__':\n", "# app.run(debug=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 方法 2. 内容作为选项卡子项\n", "除了通过回调显示内容外,您还可以将内容直接嵌入到 `Tab` 组件的 `children` 属性中:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from dash import Dash, dcc, html\n", "\n", "external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']\n", "\n", "app = Dash(__name__, external_stylesheets=external_stylesheets)\n", "\n", "app.layout = html.Div([\n", " dcc.Tabs([\n", " dcc.Tab(label='Tab one', children=[\n", " dcc.Graph(\n", " figure={\n", " 'data': [\n", " {'x': [1, 2, 3], 'y': [4, 1, 2],\n", " 'type': 'bar', 'name': 'SF'},\n", " {'x': [1, 2, 3], 'y': [2, 4, 5],\n", " 'type': 'bar', 'name': 'Montréal'},\n", " ]\n", " }\n", " )\n", " ]),\n", " dcc.Tab(label='Tab two', children=[\n", " dcc.Graph(\n", " figure={\n", " 'data': [\n", " {'x': [1, 2, 3], 'y': [1, 4, 1],\n", " 'type': 'bar', 'name': 'SF'},\n", " {'x': [1, 2, 3], 'y': [1, 2, 3],\n", " 'type': 'bar', 'name': 'Montréal'},\n", " ]\n", " }\n", " )\n", " ]),\n", " dcc.Tab(label='Tab three', children=[\n", " dcc.Graph(\n", " figure={\n", " 'data': [\n", " {'x': [1, 2, 3], 'y': [2, 4, 3],\n", " 'type': 'bar', 'name': 'SF'},\n", " {'x': [1, 2, 3], 'y': [5, 4, 3],\n", " 'type': 'bar', 'name': 'Montréal'},\n", " ]\n", " }\n", " )\n", " ]),\n", " ])\n", "])\n", "\n", "# if __name__ == '__main__':\n", "# app.run(debug=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```{tip}\n", "请注意,这种方法有一个缺点:它要求您预先计算每个选项卡的 `children` 属性,并一次性通过网络发送所有选项卡的内容。回调方法允许您在需要时(即点击选项卡时)动态计算选项卡的内容。\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 样式化选项卡组件\n", "\n", "### 使用 CSS 类\n", "\n", "通过为 `className` 属性提供自定义的 CSS 类,可以使用 CSS 类来样式化 `Tabs`(和 `Tab`)组件:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "from dash import Dash, dcc, html, Input, Output, callback\n", "\n", "app = Dash(__name__)\n", "\n", "app.layout = html.Div([\n", " dcc.Tabs(\n", " id=\"tabs-with-classes\",\n", " value='tab-2',\n", " parent_className='custom-tabs',\n", " className='custom-tabs-container',\n", " children=[\n", " dcc.Tab(\n", " label='Tab one',\n", " value='tab-1',\n", " className='custom-tab',\n", " selected_className='custom-tab--selected'\n", " ),\n", " dcc.Tab(\n", " label='Tab two',\n", " value='tab-2',\n", " className='custom-tab',\n", " selected_className='custom-tab--selected'\n", " ),\n", " dcc.Tab(\n", " label='Tab three, multiline',\n", " value='tab-3', className='custom-tab',\n", " selected_className='custom-tab--selected'\n", " ),\n", " dcc.Tab(\n", " label='Tab four',\n", " value='tab-4',\n", " className='custom-tab',\n", " selected_className='custom-tab--selected'\n", " ),\n", " ]),\n", " html.Div(id='tabs-content-classes')\n", "])\n", "\n", "@callback(Output('tabs-content-classes', 'children'),\n", " Input('tabs-with-classes', 'value'))\n", "def render_content(tab):\n", " if tab == 'tab-1':\n", " return html.Div([\n", " html.H3('Tab content 1')\n", " ])\n", " elif tab == 'tab-2':\n", " return html.Div([\n", " html.H3('Tab content 2')\n", " ])\n", " elif tab == 'tab-3':\n", " return html.Div([\n", " html.H3('Tab content 3')\n", " ])\n", " elif tab == 'tab-4':\n", " return html.Div([\n", " html.H3('Tab content 4')\n", " ])\n", "\n", "# if __name__ == '__main__':\n", "# app.run(debug=True)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "请注意,通过为 `parent_className` 属性提供一个类,我们也可以对 `Tabs` 的容器进行样式化,这里我们使用它来在下方绘制边框,将实际的 `Tabs`(带内边距)更居中地定位。我们还为常规的 `Tab` 组件添加了 `display: flex` 和 `justify-content: center`,以便多行标签不会破坏文本的流动。相应的 CSS 文件(`assets/tabs.css`)如下所示。将该文件保存在 `assets/` 文件夹中(可以根据您的需要命名)。Dash 会在加载应用时自动包含此 CSS。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```css\n", ".custom-tabs-container {\n", " width: 85%;\n", "}\n", ".custom-tabs {\n", " border-top-left-radius: 3px;\n", " background-color: #f9f9f9;\n", " padding: 0px 24px;\n", " border-bottom: 1px solid #d6d6d6;\n", "}\n", "\n", ".custom-tab {\n", " color:#586069;\n", " border-top-left-radius: 3px;\n", " border-top-right-radius: 3px;\n", " border-top: 3px solid transparent !important;\n", " border-left: 0px !important;\n", " border-right: 0px !important;\n", " border-bottom: 0px !important;\n", " background-color: #fafbfc;\n", " padding: 12px !important;\n", " font-family: \"system-ui\";\n", " display: flex !important;\n", " align-items: center;\n", " justify-content: center;\n", "}\n", ".custom-tab--selected {\n", " color: black;\n", " box-shadow: 1px 1px 0px white;\n", " border-left: 1px solid lightgrey !important;\n", " border-right: 1px solid lightgrey !important;\n", " border-top: 3px solid #e36209 !important;\n", "}\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 使用内联样式\n", "\n", "提供 CSS 类的一种替代方法是直接提供样式字典:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "from dash import Dash, dcc, html, Input, Output, callback\n", "\n", "app = Dash(__name__)\n", "\n", "tabs_styles = {\n", " 'height': '44px'\n", "}\n", "tab_style = {\n", " 'borderBottom': '1px solid #d6d6d6',\n", " 'padding': '6px',\n", " 'fontWeight': 'bold'\n", "}\n", "\n", "tab_selected_style = {\n", " 'borderTop': '1px solid #d6d6d6',\n", " 'borderBottom': '1px solid #d6d6d6',\n", " 'backgroundColor': '#119DFF',\n", " 'color': 'white',\n", " 'padding': '6px'\n", "}\n", "\n", "app.layout = html.Div([\n", " dcc.Tabs(id=\"tabs-styled-with-inline\", value='tab-1', children=[\n", " dcc.Tab(label='Tab 1', value='tab-1', style=tab_style, selected_style=tab_selected_style),\n", " dcc.Tab(label='Tab 2', value='tab-2', style=tab_style, selected_style=tab_selected_style),\n", " dcc.Tab(label='Tab 3', value='tab-3', style=tab_style, selected_style=tab_selected_style),\n", " dcc.Tab(label='Tab 4', value='tab-4', style=tab_style, selected_style=tab_selected_style),\n", " ], style=tabs_styles),\n", " html.Div(id='tabs-content-inline')\n", "])\n", "\n", "@callback(Output('tabs-content-inline', 'children'),\n", " Input('tabs-styled-with-inline', 'value'))\n", "def render_content(tab):\n", " if tab == 'tab-1':\n", " return html.Div([\n", " html.H3('Tab content 1')\n", " ])\n", " elif tab == 'tab-2':\n", " return html.Div([\n", " html.H3('Tab content 2')\n", " ])\n", " elif tab == 'tab-3':\n", " return html.Div([\n", " html.H3('Tab content 3')\n", " ])\n", " elif tab == 'tab-4':\n", " return html.Div([\n", " html.H3('Tab content 4')\n", " ])\n", "\n", "# if __name__ == '__main__':\n", "# app.run(debug=True)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "最后,您可以通过在字典中指定 `\"border\"`(边框)、`\"primary\"`(主要)和 `\"background\"`(背景)颜色来设置 `Tabs` 组件的颜色属性。如果您要使用它们,请确保都设置了!" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "from dash import Dash, dcc, html, Input, Output, callback\n", "\n", "app = Dash(__name__)\n", "\n", "app.layout = html.Div([\n", " dcc.Tabs(id=\"tabs-styled-with-props\", value='tab-1', children=[\n", " dcc.Tab(label='1', value='tab-1'),\n", " dcc.Tab(label='2', value='tab-2'),\n", " ], colors={\n", " \"border\": \"white\",\n", " \"primary\": \"gold\",\n", " \"background\": \"cornsilk\"\n", " }),\n", " html.Div(id='tabs-content-props')\n", "])\n", "\n", "@callback(Output('tabs-content-props', 'children'),\n", " Input('tabs-styled-with-props', 'value'))\n", "def render_content(tab):\n", " if tab == 'tab-1':\n", " return html.Div([\n", " html.H3('Tab content 1')\n", " ])\n", " elif tab == 'tab-2':\n", " return html.Div([\n", " html.H3('Tab content 2')\n", " ])\n", "\n", "# if __name__ == '__main__':\n", "# app.run(debug=True)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "py311", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.7" } }, "nbformat": 4, "nbformat_minor": 2 }