📌 你將學會:
Input
,Output
,State
差異與用途- 多個輸入的 callback 使用方法
- 使用者動作判斷(例如:只在按下按鈕時觸發)
- 利用
dash.callback_context
控制觸發條件 - 實作:多條國家生命線圖 + 自訂年份區間 + 按鈕觸發圖表更新
✳️ Input / Output / State 是什麼?
類別說明常見用途Input使用者觸發更新的輸入元件(會即時觸發 callback)Dropdown、SliderOutputcallback 的輸出(通常是畫面上的元件)Graph、HTML 元件State非觸發性參數(按下按鈕時才一起讀取)TextInput、Date
🛠️ 範例:選擇多國、年份區間、按下按鈕才更新圖表

import dash
from dash import dcc, html, Input, Output, State
import plotly.express as px
import pandas as pd
# 讀取資料
df = px.data.gapminder()
# 初始化 App
app = dash.Dash(__name__)
# Layout 設計
app.layout = html.Div([
html.H2("🌍 多國家壽命趨勢比較"),
dcc.Dropdown(
id='country-dropdown',
options=[{'label': c, 'value': c} for c in sorted(df['country'].unique())],
multi=True,
value=['Taiwan', 'Japan'],
placeholder="選擇一個或多個國家"
),
dcc.RangeSlider(
id='year-slider',
min=int(df['year'].min()),
max=int(df['year'].max()),
marks={int(year): str(year) for year in df['year'].unique()},
value=[1980, 2007],
step=None,
allowCross=False
),
html.Button("📈 更新圖表", id='update-button', n_clicks=0, style={'marginTop': '20px'}),
dcc.Graph(id='line-graph'),
html.Div(id='debug-output', style={'marginTop': '10px', 'color': 'gray'})
])
# Callback:當按鈕被按下時才觸發
@app.callback(
Output('line-graph', 'figure'),
Output('debug-output', 'children'),
Input('update-button', 'n_clicks'),
State('country-dropdown', 'value'),
State('year-slider', 'value')
)
def update_graph(n_clicks, selected_countries, year_range):
if not selected_countries:
return dash.no_update, "⚠️ 請選擇至少一個國家"
filtered_df = df[(df['country'].isin(selected_countries)) &
(df['year'] >= year_range[0]) &
(df['year'] <= year_range[1])]
fig = px.line(filtered_df, x='year', y='lifeExp', color='country',
title="選定國家生命期望值趨勢", markers=True)
return fig, f"✅ 已載入:{', '.join(selected_countries)}({year_range[0]} ~ {year_range[1]})"
# 啟動應用
if __name__ == '__main__':
app.run_server(debug=True)
# app.run(debug=True) # 看版本
📦📦 套件與資料準備
import dash
from dash import dcc, html, Input, Output, State
import plotly.express as px
import pandas as pd
dash
:用來建立 Web 應用的主套件。dcc
:Dash Core Components,如下拉選單、圖表等互動元件。html
:Dash 的 HTML 標籤元件,用於組合 UI。Input
,Output
,State
:用來定義 Callback 函式的輸入與輸出。plotly.express
: 簡化的 Plotly 繪圖工具。pandas
:處理資料用。
df = px.data.gapminder()
- 使用 Plotly 內建的
gapminder
資料集,包含世界各國在不同年份的人均 GDP、壽命與人口數。
🌐 App 初始化與 Layout 設計
app = dash.Dash(__name__)
- 建立一個 Dash 應用實例。
app.layout = html.Div([
...
])
- 使用
html.Div
組成整個畫面的外觀。
📌 Layout 元件細節:
標題
html.H2("🌍 多國家壽命趨勢比較"),
顯示應用的標題。
國家下拉選單(多選)
dcc.Dropdown(
id='country-dropdown',
options=[{'label': c, 'value': c} for c in sorted(df['country'].unique())],
multi=True,
value=['Taiwan', 'Japan'],
placeholder="選擇一個或多個國家"
),
- 可選多個國家。
- 選項來自
df['country'].unique()
。 - 預設選取台灣與日本。
年份範圍滑桿
dcc.RangeSlider(
id='year-slider',
min=int(df['year'].min()),
max=int(df['year'].max()),
marks={int(year): str(year) for year in df['year'].unique()},
value=[1980, 2007],
step=None,
allowCross=False
),
- 允許使用者選擇特定年份區間。
marks
會顯示在滑桿上對應的年份。step=None
表示只能選擇資料中實際存在的年份。allowCross=False
防止兩端交錯。
更新按鈕
html.Button("📈 更新圖表", id='update-button', n_clicks=0, style={'marginTop': '20px'}),
- 手動觸發圖表更新。
- 避免每次調整滑桿或下拉就重新繪圖,提升效能。
圖表顯示區域
dcc.Graph(id='line-graph'),
- 顯示以 Plotly 繪製的生命期望圖。
偵錯訊息區(顯示載入的參數)
html.Div(id='debug-output', style={'marginTop': '10px', 'color': 'gray'})
- 顯示目前選取的國家與年份範圍。
⚙️ Callback 控制邏輯
@app.callback(這段註冊一個 callback 函式
Output('line-graph', 'figure'),
Output('debug-output', 'children'),
Input('update-button', 'n_clicks'),
State('country-dropdown', 'value'),
State('year-slider', 'value')
)
update_graph
:- 只有當使用者點擊「更新圖表」按鈕時(
Input
)才觸發。 - 使用目前
Dropdown
與Slider
的選取值(State
)來產生新的圖。
📉 update_graph
函式邏輯:
def update_graph(n_clicks, selected_countries, year_range):
if not selected_countries:
return dash.no_update, "⚠️ 請選擇至少一個國家"
- 若沒選國家,則不更新圖表,並提示錯誤。
filtered_df = df[(df['country'].isin(selected_countries)) &
(df['year'] >= year_range[0]) &
(df['year'] <= year_range[1])]
- 根據選擇的國家與年份篩選資料。
fig = px.line(filtered_df, x='year', y='lifeExp', color='country',
title="選定國家生命期望值趨勢", markers=True)
- 使用 Plotly Express 繪製折線圖。
- 每個國家一條線,x 軸為年份,y 軸為壽命(life expectancy)。
return fig, f"✅ 已載入:{', '.join(selected_countries)}({year_range[0]} ~ {year_range[1]})"
- 回傳圖表與 debug 訊息。
🧠 關鍵解說
1️⃣ dcc.RangeSlider
的用法
可以讓你用滑桿選取一段時間範圍,value=[start, end]
。
dcc.RangeSlider(
id='year-slider',
min=int(df['year'].min()),
max=int(df['year'].max()),
marks={int(year): str(year) for year in df['year'].unique()},
value=[1980, 2007],
step=None,
allowCross=False
),
2️⃣ html.Button
+ Input(n_clicks)
透過 n_clicks
控制只有按下按鈕才觸發 callback,避免使用者每改一個欄位就自動重繪圖表。
# Callback:當按鈕被按下時才觸發
@app.callback(
Output('line-graph', 'figure'),
Output('debug-output', 'children'),
Input('update-button', 'n_clicks'),
State('country-dropdown', 'value'),
State('year-slider', 'value')
)

按更新圖檔才會更新

3️⃣ 使用 State
而非 Input
因為我們不想讓 Dropdown
或 Slider
的變動立即觸發 callback,所以要改用 State
來「讀值但不觸發」。
📚 總結
