Bokeh 的優點是高自由度與高效能, 但其 API 較低階, 故並未內建像 Plotly 的 CandleStick 那樣的類別, 本篇旨在為 Bokey 山寨一個 CandleStick 來簡化 K 線圖的繪製. 為了讓程式碼不至於過度複雜, 以下測試不處理非交易日 K 棒空缺問題.
4. 用封裝的類別繪製 K 線圖 :
下面範例改寫自前一篇測試的 bokeh_candlestick_3.py, 將繪製 K 線圖的邏輯封裝在自訂的 BokehChart 類別裡, 只要呼叫其建構式並傳入 title/width/height 參數建立物件, 然後在主程式中呼叫 add_candlestick(df) 即可繪製 K 線圖, 程式碼如下 :
# bokeh_candlestick_class_1.py
import yfinance as yf
import pandas as pd
from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource, HoverTool, CrosshairTool, Span
class BokehChart:
"""封裝 Bokeh K線圖與技術分析工具的繪圖類別"""
def __init__(self, title="K線圖", width=800, height=400):
# 建立並初始化畫布
self.fig=figure(
x_axis_type='datetime',
title=title,
width=width,
height=height,
tools='xpan, xwheel_zoom, box_zoom, reset, save'
)
self.fig.grid.grid_line_alpha=0.3
# 初始化虛線十字游標
self._add_crosshair()
def _add_crosshair(self):
"""內部方法:加入虛線十字游標"""
w_span=Span(dimension="width", line_dash="dashed", line_color="gray", line_alpha=0.6, line_width=1)
h_span=Span(dimension="height", line_dash="dashed", line_color="gray", line_alpha=0.6, line_width=1)
self.fig.add_tools(CrosshairTool(overlay=[w_span, h_span]))
def add_candlestick(self, df, date_col='Date', open_col='Open', high_col='High', low_col='Low', close_col='Close', vol_col='Volume'):
"""對外介面:加入 K 線圖與懸停工具"""
# 複製資料以避免修改到原始 DataFrame
data=df.copy()
# 定義漲跌顏色 (紅漲綠跌)
data['color']=['#ff0000' if c >= o else '#00aa00' for o, c in zip(data[open_col], data[close_col])]
# 建立資料來源
source=ColumnDataSource(data)
# 繪製上下影線
self.fig.segment(date_col, high_col, date_col, low_col, color="black", source=source)
# 繪製 K 棒 (12 小時寬度)
width_ms=12 * 60 * 60 * 1000
self.fig.vbar(date_col, width_ms, open_col, close_col,
fill_color='color', line_color='black', source=source)
# 加入互動式懸停工具 (HoverTool)
hover=HoverTool(
tooltips=[
("日期", f"@{date_col}{{%F}}"),
("開盤", f"@{open_col}{{0.00}}"),
("收盤", f"@{close_col}{{0.00}}"),
("最高", f"@{high_col}{{0.00}}"),
("最低", f"@{low_col}{{0.00}}"),
("成交量", f"@{vol_col}{{0,0}}")
],
formatters={f'@{date_col}': 'datetime'}
)
self.fig.add_tools(hover)
def show(self):
"""顯示圖表"""
show(self.fig)
# ==========================================
# 主程式:使用類別繪圖 (如同 Plotly 般簡潔)
# ==========================================
if __name__ == "__main__":
# 1. 下載真實 OHLCV 資料
df=yf.download('0050.tw', start='2026-03-01', end='2026-04-30', auto_adjust=True)
df.columns=df.columns.map(lambda x: x[0])
df=df.reset_index()
# 2. 實例化畫布並繪圖
chart=BokehChart(title='0050.TW 台灣50 ETF K線圖 (2026-03 ~ 2026-04)', width=800, height=400)
chart.add_candlestick(df)
# 3. 顯示圖表
chart.show()
結果如下 :
5. 用封裝的類別繪製 K 線圖 + 成交量副圖 :
如果要同時繪製 K 線圖與成交量副圖, 則須匯入 bokeh.layouts.column() 函式進行垂直布局, 讓主圖 (fig_k) 與副圖能共用 X 軸並連動. 程式碼改寫如下 :
# bokeh_candlestick_class_2.py
import yfinance as yf
import pandas as pd
from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource, HoverTool, CrosshairTool, Span, CustomJSTickFormatter
from bokeh.layouts import column # 引入垂直佈局工具
class BokehChart:
"""封裝 Bokeh K線圖與成交量子圖的繪圖類別"""
# 稍微調整初始化參數,允許分別設定主圖與副圖的高度
def __init__(self, title="K線圖", width=800, height_k=400, height_v=150):
# 1. 建立主圖 (K線圖)
self.fig_k=figure(
title=title,
width=width, height=height_k,
tools='xpan, xwheel_zoom, box_zoom, reset, save'
)
self.fig_k.grid.grid_line_alpha=0.3
self.fig_k.xaxis.visible=False # 隱藏主圖的 X 軸,讓畫面更緊湊
# 2. 建立副圖 (成交量圖)
self.fig_v=figure(
width=width, height=height_v,
x_range=self.fig_k.x_range, # 【關鍵連動】將副圖的 X 軸範圍綁定為主圖的 X 軸
tools='xpan, xwheel_zoom, reset'
)
self.fig_v.grid.grid_line_alpha=0.3
self.fig_v.y_range.start=0 # 確保成交量 Y 軸從 0 開始
# 3. 組合為垂直佈局
self.layout=column(self.fig_k, self.fig_v)
# 4. 初始化虛線十字游標 (加在兩張圖上)
self._add_crosshair(self.fig_k)
self._add_crosshair(self.fig_v)
def _add_crosshair(self, fig):
"""內部方法:為指定的畫布加入虛線十字游標"""
w_span=Span(dimension="width", line_dash="dashed", line_color="gray", line_alpha=0.6, line_width=1)
h_span=Span(dimension="height", line_dash="dashed", line_color="gray", line_alpha=0.6, line_width=1)
fig.add_tools(CrosshairTool(overlay=[w_span, h_span]))
def add_candlestick(self, df, date_col='Date', open_col='Open', high_col='High', low_col='Low', close_col='Close', vol_col='Volume'):
"""對外介面:處理資料並繪製 K 線與成交量"""
data=df.copy()
# 建立連續整數序列 (消除假日空缺)
data['seq']=range(len(data))
# 定義漲跌顏色
data['color']=['#ff0000' if c >= o else '#00aa00' for o, c in zip(data[open_col], data[close_col])]
# 用 Python 預先處理好 Bokeh 風格的日期字串
date_map={}
prev_year=None
for i, d in enumerate(data[date_col]):
label=f"{d.strftime('%b')} {d.day}"
if i == 0 or d.year != prev_year:
label += f"\n{d.year}"
date_map[str(i)]=label
prev_year=d.year
source=ColumnDataSource(data)
width_val=0.8 # 因為 X 軸變成了連續整數,寬度直接用比例 0.8
# --- 繪製主圖 (K線) ---
self.fig_k.segment('seq', high_col, 'seq', low_col, color="black", source=source)
self.fig_k.vbar('seq', width_val, open_col, close_col, fill_color='color', line_color='black', source=source)
# --- 繪製副圖 (成交量) ---
# 使用 vbar,top 參數指定為成交量欄位
self.fig_v.vbar('seq', width_val, top=vol_col, fill_color='color', line_color='black', source=source)
# --- 設定副圖的 X 軸日期標籤 ---
js_code="""
var idx=Math.round(tick).toString();
if (date_map[idx] !== undefined) {
return date_map[idx];
} else {
return "";
}
"""
self.fig_v.xaxis.formatter=CustomJSTickFormatter(code=js_code, args={'date_map': date_map})
self.fig_v.xaxis.major_label_orientation=0
self.fig_v.xaxis.major_label_standoff=15
# --- 加入互動式懸停工具 ---
hover=HoverTool(
tooltips=[
("日期", f"@{date_col}{{%F}}"),
("開盤", f"@{open_col}{{0.00}}"),
("收盤", f"@{close_col}{{0.00}}"),
("最高", f"@{high_col}{{0.00}}"),
("最低", f"@{low_col}{{0.00}}"),
("成交量", f"@{vol_col}{{0,0}}")
],
formatters={f'@{date_col}': 'datetime'},
mode='vline' # 【推薦】改為 vline 模式,滑鼠對齊同一個垂直線就會觸發
)
self.fig_k.add_tools(hover)
self.fig_v.add_tools(hover)
def show(self):
"""顯示圖表佈局"""
show(self.layout) # 【修改】現在是顯示整個 layout 而不是單一 fig
# ==========================================
# 主程式
# ==========================================
if __name__ == "__main__":
# 1. 下載實盤 OHLCV 資料
df=yf.download('0050.tw', start='2026-03-01', end='2026-04-30', auto_adjust=True)
df.columns=df.columns.map(lambda x: x[0])
df=df.reset_index()
# 2. 建立畫布並繪圖
chart=BokehChart(title='0050.TW 台灣50 ETF (含同步成交量)')
chart.add_candlestick(df)
# 3. 顯示圖表
chart.show()
注意, 此例之 BokehChart 類別還封裝了處理非交易日空缺的邏輯, 這讓整個畫布上的 K 棒看起來是連續無空缺的, 結果如下 :
6. 用封裝的類別繪製 K 線圖 + 成交量 + 技術指標副圖 :
上面範例中的 BokehChart 類別把主圖與副圖邏輯都寫在類別裡, 每次要添加技術指標副圖都要去改類別並不符合軟工原則, 若要讓程式碼具有擴充性, 必須對架構做一次重構, 要建立一個 動態串列 self.figures = [] 的來儲存所有畫布, 並增加通用副圖方法, 例如 add_bar_subplot() (畫成交量, MACD柱狀圖) 與 add_line_subplot() (畫 RSI, 均線).
程式碼如下 :
# bokeh_candlestick_class_3.py
import yfinance as yf
import pandas as pd
import pandas_ta as ta #
from bokeh.plotting import figure
from bokeh.io import show as show_bokeh # 避免名稱衝突
from bokeh.models import ColumnDataSource, HoverTool, CrosshairTool, Span, CustomJSTickFormatter
from bokeh.layouts import column
class BokehChart:
"""高擴充性 Bokeh 繪圖類別,支援動態疊加無限多個副圖"""
def __init__(self, title="K線圖", width=800, main_height=400):
self.width=width
self.title=title
self.main_height=main_height
# 動態儲存所有畫布的清單 (主圖會在 index 0)
self.figures=[]
self.source=None
self.date_map={}
self.fig_k=None # 參照主圖,用來同步 x_range
def _add_crosshair(self, fig):
"""內部方法:加入虛線十字游標"""
w_span=Span(dimension="width", line_dash="dashed", line_color="gray", line_alpha=0.6, line_width=1)
h_span=Span(dimension="height", line_dash="dashed", line_color="gray", line_alpha=0.6, line_width=1)
fig.add_tools(CrosshairTool(overlay=[w_span, h_span]))
def add_candlestick(self, df, date_col='Date', open_col='Open', high_col='High', low_col='Low', close_col='Close'):
"""【步驟 1】:初始化主圖與共用資料源"""
data=df.copy()
# 建立連續整數序列 (消除假日空缺)
data['seq']=range(len(data))
data['color']=['#ff0000' if c >= o else '#00aa00' for o, c in zip(data[open_col], data[close_col])]
# 製作日期對照表
self.date_map={}
prev_year=None
for i, d in enumerate(data[date_col]):
label=f"{d.strftime('%b')} {d.day}"
if i == 0 or d.year != prev_year:
label += f"\n{d.year}"
self.date_map[str(i)]=label
prev_year=d.year
# 建立全局共用的資料源 (此時 df 裡面已經包含外部算好的 RSI 等指標)
self.source=ColumnDataSource(data)
# 建立主畫布
self.fig_k=figure(title=self.title, width=self.width, height=self.main_height, tools='xpan, xwheel_zoom, box_zoom, reset, save')
self.fig_k.grid.grid_line_alpha=0.3
# 繪製 K 線
self.fig_k.segment('seq', high_col, 'seq', low_col, color="black", source=self.source)
self.fig_k.vbar('seq', 0.8, open_col, close_col, fill_color='color', line_color='black', source=self.source)
# 設定主圖 Hover
hover=HoverTool(
tooltips=[("日期", f"@{date_col}{{%F}}"), ("開盤", f"@{open_col}{{0.00}}"), ("收盤", f"@{close_col}{{0.00}}")],
formatters={f'@{date_col}': 'datetime'}, mode='vline'
)
self.fig_k.add_tools(hover)
self._add_crosshair(self.fig_k)
# 加入畫布清單
self.figures.append(self.fig_k)
def add_bar_subplot(self, col_name, title="", color_col=None, color='blue', height=150):
"""通用介面:新增柱狀圖副圖 (例如:成交量、MACD柱)"""
fig=figure(width=self.width, height=height, x_range=self.fig_k.x_range, tools='xpan, xwheel_zoom, reset')
fig.grid.grid_line_alpha=0.3
fig.y_range.start=0
# 若有指定顏色欄位 (如成交量的紅綠色),則使用該欄位;否則使用單一顏色
fill_c=color_col if color_col else color
fig.vbar('seq', 0.8, top=col_name, fill_color=fill_c, line_color='black', source=self.source)
# 專屬 Hover
hover=HoverTool(tooltips=[("日期", "@Date{%F}"), (title or col_name, f"@{col_name}{{0,0}}")], formatters={'@Date': 'datetime'}, mode='vline')
fig.add_tools(hover)
self._add_crosshair(fig)
self.figures.append(fig)
def add_line_subplot(self, col_name, title="", color='blue', height=150, hlines=None):
"""通用介面:新增折線圖副圖 (例如:RSI、均線、KD)"""
fig=figure(width=self.width, height=height, x_range=self.fig_k.x_range, tools='xpan, xwheel_zoom, reset')
fig.grid.grid_line_alpha=0.3
fig.line('seq', col_name, color=color, line_width=1.5, source=self.source)
# 支援加入水平參考線 (例如 RSI 的 30, 70 超買超賣線)
if hlines:
for y_val in hlines:
line=Span(location=y_val, dimension='width', line_color='gray', line_dash='dashed', line_alpha=0.5)
fig.add_layout(line)
# 專屬 Hover
hover=HoverTool(tooltips=[("日期", "@Date{%F}"), (title or col_name, f"@{col_name}{{0.00}}")], formatters={'@Date': 'datetime'}, mode='vline')
fig.add_tools(hover)
self._add_crosshair(fig)
self.figures.append(fig)
def show(self):
"""渲染圖表:隱藏除最後一張圖外的 X 軸,並套用自訂日期標籤"""
for i, fig in enumerate(self.figures):
# 不是最後一張圖,隱藏 X 軸
if i < len(self.figures) - 1:
fig.xaxis.visible=False
# 是最後一張圖,掛上 JavaScript 日期查表邏輯
else:
js_code="""
var idx=Math.round(tick).toString();
if (date_map[idx] !== undefined) {
return date_map[idx];
}
else {
return "";
}
"""
fig.xaxis.formatter=CustomJSTickFormatter(code=js_code, args={'date_map': self.date_map})
fig.xaxis.major_label_orientation=0
fig.xaxis.major_label_standoff=15
# 垂直疊加所有被加入的圖表
layout=column(*self.figures)
show_bokeh(layout)
# ==========================================
# 主程式:展現架構擴充性的時刻
# ==========================================
if __name__ == "__main__":
# 1. 獲取資料
df=yf.download('0050.tw', start='2025-10-01', end='2026-04-30', auto_adjust=True)
df.columns=df.columns.map(lambda x: x[0])
df=df.reset_index()
# 2. 在外部計算所有技術指標 (類別完全不需要知道 RSI 怎麼算的)
df['RSI']=ta.rsi(df['Close'], length=14)
# 3. 開始繪圖 (如堆積木般直覺)
chart=BokehChart(title='0050.TW 台灣50 ETF (整合 RSI 指標)')
# 初始化主圖與資料源
chart.add_candlestick(df)
# 呼叫通用介面新增成交量 (使用 color 欄位決定紅綠)
chart.add_bar_subplot('Volume', title='成交量', color_col='color', height=100)
# 呼叫通用介面新增 RSI (自訂顏色,並加上 30/70 水平參考線)
chart.add_line_subplot('RSI', title='RSI(14)', color='purple', height=150, hlines=[30, 70])
# 顯示成果
chart.show()
此例使用 pandas_ta 套件計算 RSI 指標值, 結果放進 df['RSI'] 欄位, 結果如下 :
7. 獨立的 Bokeh K 線圖模組 :
我們可以將上面範例的 BokehChart 類別寫在一個獨立的模組檔案例如 bokeh_chart.py 中, 再用 import 方式匯入 BokehChart 類別來繪製 K 線圖, 這樣主程式檔就會較簡潔了.
模組檔案 bokeh_chart.py 內容如下 :
# bokeh_chart.py
import pandas as pd
from bokeh.plotting import figure
from bokeh.io import show as show_bokeh
from bokeh.models import ColumnDataSource, HoverTool, CrosshairTool, Span, CustomJSTickFormatter
from bokeh.layouts import column
class BokehChart:
"""高擴充性 Bokeh 繪圖類別,支援動態疊加無限多個副圖"""
def __init__(self, title='K線圖', width=800, main_height=400):
self.width=width
self.title=title
self.main_height=main_height
self.figures=[]
self.source=None
self.date_map={}
self.fig_k=None
def _add_crosshair(self, fig):
"""加入虛線十字游標"""
w_span=Span(dimension='width', line_dash='dashed', line_color="gray", line_alpha=0.6, line_width=1)
h_span=Span(dimension='height', line_dash='dashed', line_color="gray", line_alpha=0.6, line_width=1)
fig.add_tools(CrosshairTool(overlay=[w_span, h_span]))
def add_candlestick(self, df, date_col='Date', open_col='Open', high_col='High', low_col='Low', close_col='Close'):
"""初始化主圖與共用資料源"""
data=df.copy()
# 建立連續整數序列與漲跌顏色
data['seq']=range(len(data))
data['color']=['#ff0000' if c >= o else '#00aa00' for o, c in zip(data[open_col], data[close_col])]
# 製作日期對照表
self.date_map={}
prev_year=None
for i, d in enumerate(data[date_col]):
label=f"{d.strftime('%b')} {d.day}"
if i == 0 or d.year != prev_year:
label += f"\n{d.year}"
self.date_map[str(i)]=label
prev_year=d.year
self.source=ColumnDataSource(data)
self.fig_k=figure(title=self.title, width=self.width, height=self.main_height, tools='xpan, xwheel_zoom, box_zoom, reset, save')
self.fig_k.grid.grid_line_alpha=0.3
self.fig_k.segment('seq', high_col, 'seq', low_col, color="black", source=self.source)
self.fig_k.vbar('seq', 0.8, open_col, close_col, fill_color='color', line_color='black', source=self.source)
hover=HoverTool(
tooltips=[("日期", f"@{date_col}{{%F}}"), ("開盤", f"@{open_col}{{0.00}}"), ("收盤", f"@{close_col}{{0.00}}")],
formatters={f'@{date_col}': 'datetime'}, mode='vline'
)
self.fig_k.add_tools(hover)
self._add_crosshair(self.fig_k)
self.figures.append(self.fig_k)
def add_bar_subplot(self, col_name, title="", color_col=None, color='blue', height=150, on_fig=None, y_min_zero=False):
"""新增柱狀圖副圖 (支援傳入畫布疊加到現有畫布)"""
# 如果有指定 on_fig,就用現成的畫布;否則建立新畫布
if on_fig:
fig=on_fig
else:
fig=figure(width=self.width, height=height, x_range=self.fig_k.x_range, tools='xpan, xwheel_zoom, reset')
fig.grid.grid_line_alpha=0.3
self.figures.append(fig) # 只有新畫布才需要加入清單
self._add_crosshair(fig) # 只有新畫布才需要加十字線
# 只有成交量這類數值絕對為正的圖表,才需要強制 Y 軸從 0 開始
if y_min_zero:
fig.y_range.start=0
fill_c=color_col if color_col else color
fig.vbar('seq', 0.8, top=col_name, fill_color=fill_c, line_color='black', source=self.source)
# 專屬 Hover
hover=HoverTool(tooltips=[("日期", "@Date{%F}"), (title or col_name, f"@{col_name}{{0.00}}")], formatters={'@Date': 'datetime'}, mode='vline')
fig.add_tools(hover)
return fig # 回傳畫布讓後續的線可以畫在它上面
def add_line_subplot(self, col_name, title="", color='blue', height=150, hlines=None, on_fig=None):
"""新增折線圖副圖 (支援疊加到現有畫布)"""
# 如果有指定 on_fig,就用現成的畫布;否則建立新畫布
if on_fig:
fig = on_fig
else:
fig = figure(width=self.width, height=height, x_range=self.fig_k.x_range, tools='xpan, xwheel_zoom, reset')
fig.grid.grid_line_alpha = 0.3
self.figures.append(fig)
self._add_crosshair(fig)
fig.line('seq', col_name, color=color, line_width=1.5, source=self.source)
if hlines:
for y_val in hlines:
line = Span(location=y_val, dimension='width', line_color='gray', line_dash='dashed', line_alpha=0.5)
fig.add_layout(line)
hover = HoverTool(tooltips=[("日期", "@Date{%F}"), (title or col_name, f"@{col_name}{{0.00}}")], formatters={'@Date': 'datetime'}, mode='vline')
fig.add_tools(hover)
return fig # 回傳畫布讓後續的線可以畫在它上面
def show(self):
"""渲染圖表"""
for i, fig in enumerate(self.figures):
if i < len(self.figures) - 1:
fig.xaxis.visible=False
else:
js_code="""
var idx=Math.round(tick).toString();
if (date_map[idx] !== undefined) {
return date_map[idx];
} else {
return "";
}
"""
fig.xaxis.formatter=CustomJSTickFormatter(code=js_code, args={'date_map': self.date_map})
fig.xaxis.major_label_orientation=0
fig.xaxis.major_label_standoff=15
layout=column(*self.figures)
show_bokeh(layout)
欲繪製 K 線圖+成交量+技術指標時, 主程式檔只要先從 bokeh_chart.py 匯入 BokehChart 類別並建立 BokehChart 物件後, 呼叫 add_candlestick() 繪製主圖 (K 線圖), 呼叫 add_bar_subplot() 或 add_line_subplot() 添加副圖 (成交量 & 技術指標), 下面範例添加了 RSI 與 MACD 副圖 :
# bokeh_candlestick_class_4.py
import yfinance as yf
import pandas as pd
import pandas_ta as ta
from bokeh_chart import BokehChart
if __name__ == "__main__":
# 1. 獲取資料
df=yf.download('0050.tw', start='2025-10-01', end='2026-04-30', auto_adjust=True)
df.columns=df.columns.map(lambda x: x[0])
df=df.reset_index()
# 2. 計算技術指標
# RSI
df['RSI']=ta.rsi(df['Close'], length=14)
# MACD (將計算結果合併回原本的 df)
macd_df=ta.macd(df['Close'])
df=pd.concat([df, macd_df], axis=1)
# 計算 MACD 柱狀圖的顏色 (大於等於 0 紅色,小於 0 綠色)
df['macd_color']=['#ff0000' if m >= 0 else '#00aa00' for m in df['MACDh_12_26_9']]
# 3. 畫圖
# 畫主圖 : K 線圖
chart=BokehChart(title='0050.TW 台灣50 ETF (整合 MACD)')
chart.add_candlestick(df)
# 畫成交量副圖 (加上 y_min_zero=True,避免長條圖懸空)
chart.add_bar_subplot('Volume', title='成交量', color_col='color', height=100, y_min_zero=True)
# ==========================================
# 繪製 MACD 複合副圖
# ==========================================
# 第一步:先畫 MACD 柱狀圖,並用變數 macd_fig 把畫布接住
macd_fig=chart.add_bar_subplot('MACDh_12_26_9', title='MACD柱', color_col='macd_color', height=150)
# 第二步:把 MACD 線畫在 macd_fig 身上
chart.add_line_subplot('MACD_12_26_9', title='MACD線', color='blue', on_fig=macd_fig)
# 第三步:把 Signal 線也畫在 macd_fig 身上
chart.add_line_subplot('MACDs_12_26_9', title='Signal線', color='orange', on_fig=macd_fig)
# ==========================================
chart.show()
注意, 此處 MACD 指標因為含有快線, 慢線, 與柱狀圖三個圖形, 通常都是疊在一個畫布上, 所以要分成三個步驟把三個圖層疊上去, 首先繪製柱狀圖, 然後將傳回的畫布物件 macd_fig 在後續繪製快線與慢線時傳給 on_fonfig 參數, 這樣三張圖就會疊在同一張畫布上了.
結果如下 :
可見將繪圖類別獨立出去後, 主程式結構就清爽易讀多了.




沒有留言 :
張貼留言