早上完成 kbar.py 程式改版, 經測試確認可在 Windows 與 Colab 平台上順利繪製有中文的 K 線圖後才匆匆出門去市集買菜. 中午飯後繼續趕工, 看看能否在其他 Linux 主機 (我手上只有 Mapleboard 的 Ubuntu Mate 與樹莓派 Rasbian) 上繪製有中文的 K 線圖. 雖然 Colab 的虛擬機也是 Linux, 但它與一般 Linux 在中文字型安裝上不同, 必須另外處理.
我繼續與 Claude 協作, 但這次它有點秀逗, 先後測試了 Claude 因應 Linux 環境而改寫的兩款程式碼都失敗, 連在 Colab 上跑都倒退無法顯示中文, 由於程式碼比先前 Windows + Colab 可運作版還複雜, 實在不想一行一行檢查哪裡出錯, 乾脆回 ChatGPT, 把前一篇的 Windows + Colab 可運作版程式碼丟給它, 請它在此基礎上添加可在一般 Linux 平台順利執行的 kbar.py 程式碼 :
# kbar.py
import mplfinance as mpf
import matplotlib as mpl
from matplotlib import font_manager
import os
import sys
import subprocess
def install_noto_cjk_linux(): # 在 Linux (Ubuntu/樹莓派) 安裝 Noto CJK 字體
try:
print('偵測到 Linux 環境,嘗試安裝 Noto CJK 字體...')
result=subprocess.run(
['sudo', 'apt-get', 'install', '-y', 'fonts-noto-cjk'],
capture_output=True,
text=True
)
if result.returncode != 0:
print(f'字體安裝失敗: {result.stderr}')
return None
# 更新 font cache
subprocess.run(['fc-cache', '-fv'], capture_output=True)
# 強制刷新 matplotlib 字體快取
font_manager._load_fontmanager(try_read_cache=False)
print('Noto CJK 字體安裝完成')
return True
except Exception as e:
print(f'安裝過程出錯: {e}')
return None
def download_noto_cjk_colab(): # 下載中文字體到 Colab 環境
font_path='/content/NotoSansCJK-Regular.ttc'
if not os.path.exists(font_path):
print("正在下載中文字體...")
url='https://github.com/googlefonts/noto-cjk/raw/main/' +\
'Sans/OTC/NotoSansCJK-Regular.ttc'
try:
result=subprocess.run( # 使用 wget 下載字體
['wget', '-O', font_path, url],
capture_output=True,
text=True
)
if result.returncode != 0:
print(f'下載失敗: {result.stderr}')
return None
print('中文字體下載完成')
return font_path
except Exception as e:
print(f'字體下載失敗: {e}')
return None
else:
print('字體檔案已存在')
return font_path
def register_font_colab(font_path): # 註冊字體並回傳可用的字體名稱
if not font_path or not os.path.exists(font_path):
return None
try: # 註冊字體
font_manager.fontManager.addfont(font_path)
# 重建字體管理器快取
font_manager._load_fontmanager(try_read_cache=False)
# 檢查註冊後可用的字體名稱
fonts={f.name for f in font_manager.fontManager.ttflist}
# Noto Sans CJK 可能的名稱變化
possible_names=[
'Noto Sans CJK TC',
'Noto Sans CJK JP',
'Noto Sans CJK SC',
'Noto Sans CJK KR',
'Noto Sans CJK'
]
for name in possible_names:
if name in fonts:
print(f'成功註冊字體: {name}')
return name
print(f"字體註冊後可用名稱: {[f for f in fonts if 'Noto' in f or 'CJK' in f]}")
return None
except Exception as e:
print(f"字體註冊失敗: {e}")
return None
def detect_font(): # 偵測系統中是否有常見的中文字型
fonts={f.name for f in font_manager.fontManager.ttflist}
# 檢查常見中文字體
linux_chinese_fonts=['Noto Sans CJK TC','Noto Sans CJK JP','Noto Sans CJK SC']
if 'Microsoft JhengHei' in fonts:
return 'Microsoft JhengHei'
elif 'PingFang TC' in fonts:
return 'PingFang TC'
elif any(name in fonts for name in linux_chinese_fonts):
return next(name for name in linux_chinese_fonts if name in fonts)
else: # 檢測環境
in_colab='google.colab' in sys.modules
in_linux=sys.platform.startswith('linux')
if in_colab:
print('Colab 環境偵測到,正在下載並設定中文字體...')
font_path=download_noto_cjk_colab()
if font_path:
return register_font_colab(font_path)
elif in_linux:
print('Linux 環境偵測到,嘗試安裝 Noto CJK 字體...')
if install_noto_cjk_linux():
# 再檢查一次
fonts={f.name for f in font_manager.fontManager.ttflist}
for name in linux_chinese_fonts:
if name in fonts:
return name
return None
def check_font(font): # 檢查指定字型是否存在並回傳候選清單
fonts={f.name for f in font_manager.fontManager.ttflist}
candidates=[]
if font and font in fonts:
candidates.append(font)
print(f'使用指定字體: {font}')
elif font:
print(f"[警告] 找不到字型 '{font}',將使用 fallback 字型")
# 通用 fallback 清單
fallbacks=[
'Microsoft JhengHei',
'PingFang TC',
'Noto Sans CJK TC',
'Noto Sans CJK JP',
'Noto Sans CJK SC',
'DejaVu Sans',
'Liberation Sans',
'Arial',
'sans-serif'
]
for fallback in fallbacks:
if fallback in fonts and fallback not in candidates:
candidates.append(fallback)
if candidates:
print(f'字體候選清單: {candidates[:3]}') # 只顯示前3個
return candidates
else:
print('警告: 沒有找到合適的字體')
return ['DejaVu Sans']
class KBar():
def __init__(self, df, font=None):
self.df=df
self.addplots=[]
self.font=font or detect_font()
# 設定 matplotlib 全域參數
if self.font:
# 清除舊的字體設定
if 'font.sans-serif' in mpl.rcParams:
current_fonts=mpl.rcParams['font.sans-serif'].copy()
# 移除可能造成問題的字體
current_fonts=[f for f in current_fonts if f not in ['SimHei']]
mpl.rcParams['font.sans-serif']=[self.font] + current_fonts
else:
mpl.rcParams['font.sans-serif']=[self.font, 'DejaVu Sans']
mpl.rcParams['axes.unicode_minus']=False
print(f'設定字體為: {self.font}')
else:
print('警告: 未找到中文字體,中文可能無法正常顯示')
def addplot(self, data, **kwargs):
plot=mpf.make_addplot(data, **kwargs)
self.addplots.append(plot)
def plot(self, embedding=False, **kwargs):
color=mpf.make_marketcolors(
up='red',
down='green',
inherit=True
)
# 取得字體候選清單
font_candidates=check_font(self.font)
style=mpf.make_mpf_style(
base_mpf_style='default',
marketcolors=color,
rc={
'font.family': font_candidates,
'font.sans-serif': font_candidates,
'axes.unicode_minus': False
}
)
kwargs['type']='candle'
kwargs['style']=style
kwargs['addplot']=self.addplots
result=mpf.plot(self.df, **kwargs)
if kwargs.get('returnfig', False):
return result
可見它並沒有大幅改寫程式架構, 僅新增了一個 Linux 平台下載與安裝中文字型的函式 install_noto_cjk_linux(), 以及修改了一部分的 detect_font() 函式內容以納入 Linux 平台的偵測程式碼而已. 為了較好地區別函式功能, 我把前一版本 (Windows + Colab) 中的 register_font() 改名為 register_font_colab(); 把 download_chinese_font() 函式改名為 download_noto_cjk_colab(). 其他函式都沒變.
先在 Windows 與 Colab 平台上測試此全平台版的 kbar.py, 結果與前一版本相同, 都能正常在 K 線圖上顯示中文, 在此略過. 接下來便是重頭戲, 要到 Linux 主機上測試, 我的 Mapleboard 系統為 Ubuntu Mate, 之前尚未安裝過 yfinance 與 mplfinance, 所以先安裝此二套件 :
安裝 yfinance :
tony1966@LX2438:~/python$ pip3 install yfinance
Defaulting to user installation because normal site-packages is not writeable
Collecting yfinance
Downloading yfinance-0.2.65-py2.py3-none-any.whl (119 kB)
... (略) ...
Successfully built multitasking peewee
Installing collected packages: pytz, peewee, multitasking, pycparser, frozendict, beautifulsoup4, cffi, curl_cffi, yfinance
Successfully installed beautifulsoup4-4.13.4 cffi-1.17.1 curl_cffi-0.13.0 frozendict-2.4.6 multitasking-0.0.12 peewee-3.18.2 pycparser-2.22 pytz-2025.2 yfinance-0.2.65
安裝 mplfinance :
tony1966@LX2438:~/python$ pip3 install mplfinance
Defaulting to user installation because normal site-packages is not writeable
Collecting mplfinance
Downloading mplfinance-0.12.10b0-py3-none-any.whl (75 kB)
... (略) ...
Installing collected packages: kiwisolver, fonttools, cycler, contourpy, matplotlib, mplfinance
Successfully installed contourpy-1.3.2 cycler-0.12.1 fonttools-4.59.1 kiwisolver-1.4.9 matplotlib-3.10.5 mplfinance-0.12.10b0
開啟 Thonny 於交談視窗輸入下列程式碼繪製 K 線圖 :
from kbar import KBar
import yfinance as yf
df=yf.download('0050.TW', start='2024-07-01', end='2024-08-21', auto_adjust=False)
df.columns=df.columns.map(lambda x: x[0]) # 改成舊版單層索引
kb=KBar(df) # 未傳 font 參數使用預設字型
kb.plot(title='台灣五十(0050.TW)', volume=True)
>>> from kbar import KBar
Matplotlib is building the font cache; this may take a moment.
>>> import yfinance as yf
>>> df=yf.download('0050.TW', start='2024-07-01', end='2024-08-21', auto_adjust=False)
[*********************100%***********************] 1 of 1 completed
>>> df.columns=df.columns.map(lambda x: x[0])
>>> kb=KBar(df)
設定字體為: Noto Sans CJK JP
>>> kb.plot(title='台灣五十(0050.TW)', volume=True)
使用指定字體: Noto Sans CJK JP
字體候選清單: ['Noto Sans CJK JP', 'DejaVu Sans', 'Liberation Sans']
可見 Ubuntu Mate 下載安裝了 Noto Sans CJK 字型來顯示中文, 結果如下 :
OK, kbar.py 模組的改版工作至此就完工啦! 可以進行套件打包發佈了.

沒有留言 :
張貼留言