2022年3月5日 星期六

如何在樹莓派用 Matplotlib 繪圖時顯示中文

過去這一周整理 Matplotlib 學習筆記, 不僅寫完進階篇, 還回頭增修基礎篇. 進階篇中關於中文顯示的問題在 Windows 很取巧地直接在程式中用 plr.rcParams() 指定內建的微軟正黑體或雅黑體, 不費甚麼力就輕鬆搞定, 參考 :


但這做法在 Linux 就不靈了, 因為 Linux 中沒有內建這兩種中文字型, 必須老實地去下載字型檔, 然後修改 Matplotlib 設定檔才行, 以下是我在鄉下那台 Pi 3 上的測試紀錄. 

樹莓派的 matplotlib 內建的 ttf 字型可用 matplotlib.font_manager.fontManager 類別的 ttflist 屬性來查詢, 裡面用串列紀錄了代表全部 ttf 字型的 Font 物件, 例如 : 

pi@raspberrypi:~ $ python    
Python 3.5.3 (default, Sep 27 2018, 17:25:39)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from matplotlib.font_manager import fontManager     
>>> for ttf in fontManager.ttflist:    
...     print(ttf)    
...
<Font 'STIXSizeFourSym' (STIXSizFourSymBol.ttf) normal normal bold normal>
<Font 'cmtt10' (cmtt10.ttf) normal normal 400 normal>
<Font 'STIXNonUnicode' (STIXNonUniBolIta.ttf) italic normal bold normal>
<Font 'STIXSizeTwoSym' (STIXSizTwoSymReg.ttf) normal normal regular normal>
<Font 'STIXSizeThreeSym' (STIXSizThreeSymBol.ttf) normal normal bold normal>
<Font 'cmex10' (cmex10.ttf) normal normal 400 normal>
<Font 'STIXNonUnicode' (STIXNonUniBol.ttf) normal normal bold normal>
<Font 'DejaVu Serif' (DejaVuSerif-Italic.ttf) italic normal 400 normal>
<Font 'STIXSizeThreeSym' (STIXSizThreeSymReg.ttf) normal normal regular normal>
<Font 'cmb10' (cmb10.ttf) normal normal 400 normal>
<Font 'cmmi10' (cmmi10.ttf) normal normal 400 normal>
<Font 'DejaVu Sans Mono' (DejaVuSansMono-Bold.ttf) normal normal bold normal>
<Font 'DejaVu Sans' (DejaVuSans-Oblique.ttf) oblique normal 400 normal>
<Font 'cmr10' (cmr10.ttf) normal normal 400 normal>
<Font 'STIXGeneral' (STIXGeneral.ttf) normal normal regular normal>
<Font 'DejaVu Sans Display' (DejaVuSansDisplay.ttf) normal normal 400 normal>
<Font 'DejaVu Sans' (DejaVuSans.ttf) normal normal 400 normal>
<Font 'cmss10' (cmss10.ttf) normal normal 400 normal>
<Font 'DejaVu Sans' (DejaVuSans-Bold.ttf) normal normal bold normal>
<Font 'STIXSizeOneSym' (STIXSizOneSymReg.ttf) normal normal regular normal>
<Font 'DejaVu Sans Mono' (DejaVuSansMono-Oblique.ttf) oblique normal 400 normal>
<Font 'STIXGeneral' (STIXGeneralBolIta.ttf) italic normal bold normal>
<Font 'DejaVu Sans Mono' (DejaVuSansMono-BoldOblique.ttf) oblique normal bold normal>
<Font 'STIXGeneral' (STIXGeneralBol.ttf) normal normal bold normal>
<Font 'STIXNonUnicode' (STIXNonUniIta.ttf) italic normal 400 normal>
<Font 'STIXSizeFourSym' (STIXSizFourSymReg.ttf) normal normal regular normal>
<Font 'DejaVu Serif' (DejaVuSerif-Bold.ttf) normal normal bold normal>
<Font 'cmsy10' (cmsy10.ttf) normal normal 400 normal>
<Font 'DejaVu Serif' (DejaVuSerif.ttf) normal normal 400 normal>
<Font 'STIXGeneral' (STIXGeneralItalic.ttf) italic normal 400 normal>
<Font 'DejaVu Serif Display' (DejaVuSerifDisplay.ttf) normal normal 400 normal>
<Font 'STIXSizeOneSym' (STIXSizOneSymBol.ttf) normal normal bold normal>
<Font 'STIXSizeTwoSym' (STIXSizTwoSymBol.ttf) normal normal bold normal>
<Font 'STIXNonUnicode' (STIXNonUni.ttf) normal normal regular normal>
<Font 'DejaVu Sans' (DejaVuSans-BoldOblique.ttf) oblique normal bold normal>
<Font 'DejaVu Sans Mono' (DejaVuSansMono.ttf) normal normal 400 normal>
<Font 'STIXSizeFiveSym' (STIXSizFiveSymReg.ttf) normal normal regular normal>
<Font 'DejaVu Serif' (DejaVuSerif-BoldItalic.ttf) italic normal bold normal>
<Font 'Caladea' (Caladea-Italic.ttf) italic normal 400 normal>
<Font 'PibotoLt' (PibotoLtItalic.ttf) italic normal 400 normal>
<Font 'FreeSans' (FreeSansBoldOblique.ttf) oblique normal bold normal>
<Font 'Lato' (Lato-Hairline.ttf) normal normal 400 normal>
<Font 'Liberation Mono' (LiberationMono-BoldItalic.ttf) italic normal bold normal>
<Font 'Liberation Mono' (LiberationMono-Italic.ttf) italic normal 400 normal>
<Font 'Piboto' (Piboto-BoldItalic.ttf) italic normal bold normal>
<Font 'Carlito' (Carlito-Regular.ttf) normal normal 400 normal>
<Font 'Linux Libertine O' (LinLibertine_R.otf) normal normal 400 normal>
<Font 'Piboto Condensed' (PibotoCondensed-Italic.ttf) italic normal 400 condensed>
<Font 'FreeSerif' (FreeSerifBoldItalic.ttf) italic normal bold normal>
<Font 'Piboto Condensed' (PibotoCondensed-Regular.ttf) normal normal regular condensed>
<Font 'Liberation Sans Narrow' (LiberationSansNarrow-BoldItalic.ttf) italic normal bold condensed>
<Font 'Caladea' (Caladea-Regular.ttf) normal normal 400 normal>
<Font 'FreeMono' (FreeMono.ttf) normal normal 400 normal>
<Font 'Piboto' (Piboto-Italic.ttf) italic normal 400 normal>
<Font 'DejaVu Serif' (DejaVuSerifCondensed.ttf) normal normal 400 condensed>
<Font 'Lato' (Lato-ThinItalic.ttf) italic normal 400 normal>
<Font 'Gentium Basic' (GenBasI.ttf) italic normal 400 normal>
<Font 'Carlito' (Carlito-Italic.ttf) italic normal 400 normal>
<Font 'Lato' (Lato-SemiboldItalic.ttf) italic normal bold normal>
<Font 'DejaVu Sans' (DejaVuSans-ExtraLight.ttf) normal normal light normal>
<Font 'FreeSans' (FreeSans.ttf) normal normal 400 normal>
<Font 'Piboto' (Piboto-Light.ttf) normal normal light normal>
<Font 'Gentium Book Basic' (GenBkBasR.ttf) normal normal book normal>
<Font 'FreeMono' (FreeMonoBold.ttf) normal normal bold normal>
<Font 'FreeSerif' (FreeSerifItalic.ttf) italic normal 400 normal>
<Font 'Linux Libertine O' (LinLibertine_RI.otf) italic normal 400 normal>
<Font 'Lato' (Lato-HairlineItalic.ttf) italic normal 400 normal>
<Font 'Liberation Sans' (LiberationSans-Bold.ttf) normal normal bold normal>
<Font 'Liberation Serif' (LiberationSerif-Italic.ttf) italic normal 400 normal>
<Font 'Lato' (Lato-Regular.ttf) normal normal regular normal>
<Font 'Piboto Condensed' (PibotoCondensed-BoldItalic.ttf) italic normal bold condensed>
<Font 'Liberation Serif' (LiberationSerif-Bold.ttf) normal normal bold normal>
<Font 'Lato' (Lato-Light.ttf) normal normal light normal>
<Font 'Lato' (Lato-Thin.ttf) normal normal 400 normal>
<Font 'Liberation Mono' (LiberationMono-Bold.ttf) normal normal bold normal>
<Font 'DejaVu Sans' (DejaVuSansCondensed-Oblique.ttf) oblique normal 400 condensed>
<Font 'DejaVu Serif' (DejaVuSerifCondensed-BoldItalic.ttf) italic normal bold condensed>
<Font 'FreeSerif' (FreeSerif.ttf) normal normal 400 normal>
<Font 'Caladea' (Caladea-Bold.ttf) normal normal bold normal>
<Font 'Liberation Sans' (LiberationSans-Italic.ttf) italic normal 400 normal>
<Font 'Liberation Sans' (LiberationSans-BoldItalic.ttf) italic normal bold normal>
<Font 'Carlito' (Carlito-Bold.ttf) normal normal bold normal>
<Font 'Lato' (Lato-BlackItalic.ttf) italic normal black normal>
<Font 'Linux Libertine Initials O' (LinLibertine_I.otf) normal normal 400 normal>
<Font 'FreeSans' (FreeSansOblique.ttf) oblique normal 400 normal>
<Font 'Liberation Sans Narrow' (LiberationSansNarrow-Italic.ttf) italic normal 400 condensed>
<Font 'Piboto' (Piboto-Thin.ttf) normal normal 400 normal>
<Font 'DejaVu Serif' (DejaVuSerifCondensed-Italic.ttf) italic normal 400 condensed>
<Font 'OpenSymbol' (opens___.ttf) normal normal 400 normal>
<Font 'Liberation Mono' (LiberationMono-Regular.ttf) normal normal 400 normal>
<Font 'PibotoLt' (PibotoLtBold.ttf) normal normal bold normal>
<Font 'Gentium' (Gentium-R.ttf) normal normal 400 normal>
<Font 'Liberation Sans' (LiberationSans-Regular.ttf) normal normal 400 normal>
<Font 'Lato' (Lato-Italic.ttf) italic normal 400 normal>
<Font 'Lato' (Lato-BoldItalic.ttf) italic normal bold normal>
<Font 'Lato' (Lato-MediumItalic.ttf) italic normal medium normal>
<Font 'Linux Libertine O' (LinLibertine_RB.otf) normal normal bold normal>
<Font 'Gentium Book Basic' (GenBkBasBI.ttf) italic normal bold normal>
<Font 'Gentium' (Gentium-I.ttf) italic normal 400 normal>
<Font 'Lato' (Lato-Heavy.ttf) normal normal heavy normal>
<Font 'Liberation Serif' (LiberationSerif-BoldItalic.ttf) italic normal bold normal>
<Font 'Lato' (Lato-Medium.ttf) normal normal medium normal>
<Font 'Lato' (Lato-LightItalic.ttf) italic normal light normal>
<Font 'FreeSerif' (FreeSerifBold.ttf) normal normal bold normal>
<Font 'Lato' (Lato-HeavyItalic.ttf) italic normal heavy normal>
<Font 'DejaVu Serif' (DejaVuSerifCondensed-Bold.ttf) normal normal bold condensed>
<Font 'Linux Libertine O' (LinLibertine_RBI.otf) italic normal bold normal>
<Font 'GentiumAlt' (GentiumAlt-R.ttf) normal normal 400 normal>
<Font 'Gentium Book Basic' (GenBkBasB.ttf) normal normal bold normal>
<Font 'Liberation Serif' (LiberationSerif-Regular.ttf) normal normal 400 normal>
<Font 'GentiumAlt' (GentiumAlt-I.ttf) italic normal 400 normal>
<Font 'Linux Libertine Mono O' (LinLibertine_M.otf) normal normal 400 normal>
<Font 'Gentium Book Basic' (GenBkBasI.ttf) italic normal book normal>
<Font 'Lato' (Lato-Bold.ttf) normal normal bold normal>
<Font 'Carlito' (Carlito-BoldItalic.ttf) italic normal bold normal>
<Font 'Linux Biolinum O' (LinBiolinum_R.otf) normal normal 400 normal>
<Font 'FreeMono' (FreeMonoOblique.ttf) oblique normal 400 normal>
<Font 'Liberation Sans Narrow' (LiberationSansNarrow-Regular.ttf) normal normal 400 condensed>
<Font 'Linux Libertine O' (LinLibertine_RZ.otf) normal normal bold normal>
<Font 'FreeMono' (FreeMonoBoldOblique.ttf) oblique normal bold normal>
<Font 'PibotoLt' (PibotoLtBoldItalic.ttf) italic normal bold normal>
<Font 'Gentium Basic' (GenBasR.ttf) normal normal 400 normal>
<Font 'DejaVu Sans' (DejaVuSansCondensed-BoldOblique.ttf) oblique normal bold condensed>
<Font 'Noto Mono' (NotoMono-Regular.ttf) normal normal 400 normal>
<Font 'Liberation Sans Narrow' (LiberationSansNarrow-Bold.ttf) normal normal bold condensed>
<Font 'Caladea' (Caladea-BoldItalic.ttf) italic normal bold normal>
<Font 'Lato' (Lato-Semibold.ttf) normal normal bold normal>
<Font 'Piboto' (Piboto-LightItalic.ttf) italic normal light normal>
<Font 'Piboto' (Piboto-Bold.ttf) normal normal bold normal>
<Font 'Linux Biolinum Keyboard O' (LinBiolinum_K.otf) normal normal 400 normal>
<Font 'Linux Biolinum O' (LinBiolinum_RI.otf) italic normal 400 normal>
<Font 'PibotoLt' (PibotoLt-Regular.ttf) normal normal regular normal>
<Font 'DejaVu Sans' (DejaVuSansCondensed.ttf) normal normal 400 condensed>
<Font 'Piboto' (Piboto-Regular.ttf) normal normal regular normal>
<Font 'FreeSans' (FreeSansBold.ttf) normal normal bold normal>
<Font 'DejaVu Math TeX Gyre' (DejaVuMathTeXGyre.ttf) normal normal regular normal>
<Font 'Piboto' (Piboto-ThinItalic.ttf) italic normal 400 normal>
<Font 'Linux Libertine O' (LinLibertine_RZI.otf) italic normal bold normal>
<Font 'DejaVu Sans' (DejaVuSansCondensed-Bold.ttf) normal normal bold condensed>
<Font 'Gentium Basic' (GenBasBI.ttf) italic normal bold normal>
<Font 'Lato' (Lato-Black.ttf) normal normal black normal>
<Font 'Gentium Basic' (GenBasB.ttf) normal normal bold normal>
<Font 'Linux Biolinum O' (LinBiolinum_RB.otf) normal normal bold normal>
<Font 'Linux Libertine Display O' (LinLibertine_DR.otf) normal normal 400 normal>
<Font 'Droid Sans Fallback' (DroidSansFallbackFull.ttf) normal normal 400 normal>
<Font 'Piboto Condensed' (PibotoCondensed-Bold.ttf) normal normal bold condensed>
>>>

可見內建的 ttf 字型中並無中文字型, 必須自己下載中文字型檔, 然後修改 matplotlib 設定檔中的預設字型, 讓它能抓到所下載的中文字型. 這個 matplotlib 設定檔位置可用 matplotlib.matplotlib_fname() 查詢 :

>>> import matplotlib     
>>> matplotlib.matplotlib_fname()    
'/home/pi/.local/lib/python3.5/site-packages/matplotlib/mpl-data/matplotlibrc'    

此設定檔是純文字檔, 可用 vim 或 nano 等開啟編輯. 以下是讓 matplotlib 能顯示中文的作法: 


1. 下載微軟正黑體 TTF 字型檔 :    

參考下面這篇文章, 作者是以微軟正黑體為例 :
 

開啟樹莓派瀏覽器, 點下列網址下載微軟正黑體 ttf 字型檔 (msj.ttf, 20.7MB) :


參考 : 


預設會下載到 Downloads 目錄下, Matplotlib 的字型都放在 Python 安裝目錄下的 site-packages/matplotlib/mpl-data/fonts/ttf 子目錄下, 上面利用 matplotlib.matplotlib_fname() 指令已知我樹莓派安裝目錄為 /home/pi/.local/lib/python3.5, 因此要將所下載的正黑體字型檔 msj.ttf 複製或搬移到下列子目錄下 : 

/home/pi/.local/lib/python3.5/site-packages/matplotlib/mpl-data/fonts/ttf

指令如下 :

pi@raspberrypi:~ $ mv Downloads/msj.ttf .local/lib/python3.5/site-packages/matplotlib/mpl-data/fonts/ttf/msj.ttf   
pi@raspberrypi:~ $ ls -ls .local/lib/python3.5/site-packages/matplotlib/mpl-data/fonts/ttf     
總計 27992
   28 -rw-r--r-- 1 pi pi    25680  4月 14  2019 cmb10.ttf
   24 -rw-r--r-- 1 pi pi    21092  4月 14  2019 cmex10.ttf
   32 -rw-r--r-- 1 pi pi    32560  4月 14  2019 cmmi10.ttf
   28 -rw-r--r-- 1 pi pi    26348  4月 14  2019 cmr10.ttf
   20 -rw-r--r-- 1 pi pi    20376  4月 14  2019 cmss10.ttf
   32 -rw-r--r-- 1 pi pi    29396  4月 14  2019 cmsy10.ttf
   28 -rw-r--r-- 1 pi pi    28136  4月 14  2019 cmtt10.ttf
  628 -rw-r--r-- 1 pi pi   641720  4月 14  2019 DejaVuSans-BoldOblique.ttf
  688 -rw-r--r-- 1 pi pi   704128  4月 14  2019 DejaVuSans-Bold.ttf
   28 -rw-r--r-- 1 pi pi    25712  4月 14  2019 DejaVuSansDisplay.ttf
  248 -rw-r--r-- 1 pi pi   253116  4月 14  2019 DejaVuSansMono-BoldOblique.ttf
  324 -rw-r--r-- 1 pi pi   331536  4月 14  2019 DejaVuSansMono-Bold.ttf
  248 -rw-r--r-- 1 pi pi   251472  4月 14  2019 DejaVuSansMono-Oblique.ttf
  336 -rw-r--r-- 1 pi pi   340240  4月 14  2019 DejaVuSansMono.ttf
  620 -rw-r--r-- 1 pi pi   633840  4月 14  2019 DejaVuSans-Oblique.ttf
  740 -rw-r--r-- 1 pi pi   756072  4月 14  2019 DejaVuSans.ttf
  340 -rw-r--r-- 1 pi pi   347064  4月 14  2019 DejaVuSerif-BoldItalic.ttf
  348 -rw-r--r-- 1 pi pi   355692  4月 14  2019 DejaVuSerif-Bold.ttf
   16 -rw-r--r-- 1 pi pi    14300  4月 14  2019 DejaVuSerifDisplay.ttf
  340 -rw-r--r-- 1 pi pi   345612  4月 14  2019 DejaVuSerif-Italic.ttf
  372 -rw-r--r-- 1 pi pi   379740  4月 14  2019 DejaVuSerif.ttf
    8 -rw-r--r-- 1 pi pi     4816  4月 14  2019 LICENSE_DEJAVU
    8 -rw-r--r-- 1 pi pi     5475  4月 14  2019 LICENSE_STIX
    4 -rw-r--r-- 1 pi pi      988  4月 14  2019 local.conf
21160 -rw-r--r-- 1 pi pi 21663376  3月  5 13:10 msj.ttf   
  180 -rw-r--r-- 1 pi pi   181152  4月 14  2019 STIXGeneralBolIta.ttf
  232 -rw-r--r-- 1 pi pi   237360  4月 14  2019 STIXGeneralBol.ttf
  172 -rw-r--r-- 1 pi pi   175040  4月 14  2019 STIXGeneralItalic.ttf
  440 -rw-r--r-- 1 pi pi   448228  4月 14  2019 STIXGeneral.ttf
   44 -rw-r--r-- 1 pi pi    41272  4月 14  2019 STIXNonUniBolIta.ttf
   32 -rw-r--r-- 1 pi pi    30512  4月 14  2019 STIXNonUniBol.ttf
   48 -rw-r--r-- 1 pi pi    46752  4月 14  2019 STIXNonUniIta.ttf
   60 -rw-r--r-- 1 pi pi    59108  4月 14  2019 STIXNonUni.ttf
   16 -rw-r--r-- 1 pi pi    13656  4月 14  2019 STIXSizFiveSymReg.ttf
   12 -rw-r--r-- 1 pi pi    12228  4月 14  2019 STIXSizFourSymBol.ttf
   16 -rw-r--r-- 1 pi pi    15972  4月 14  2019 STIXSizFourSymReg.ttf
   16 -rw-r--r-- 1 pi pi    12556  4月 14  2019 STIXSizOneSymBol.ttf
   20 -rw-r--r-- 1 pi pi    19760  4月 14  2019 STIXSizOneSymReg.ttf
   12 -rw-r--r-- 1 pi pi    12192  4月 14  2019 STIXSizThreeSymBol.ttf
   16 -rw-r--r-- 1 pi pi    15836  4月 14  2019 STIXSizThreeSymReg.ttf
   12 -rw-r--r-- 1 pi pi    12116  4月 14  2019 STIXSizTwoSymBol.ttf
   16 -rw-r--r-- 1 pi pi    15704  4月 14  2019 STIXSizTwoSymReg.ttf

可見 msj.ttf 已放進 ttf 子目錄了. 


2. 編輯 matplotlib 字型設定檔 :    

使用 nano 編輯 matplotlib 設定檔 : 

pi@raspberrypi:~ $ nano  .local/lib/python3.5/site-packages/matplotlib/mpl-data/matplotlibrc    

找到 '#font.family: sans-serif' 這一列, 把 font.family 前面的 # 刪除, 再往下一點找到 '#font.serif :' 這一列, 同樣是把前面的 # 刪除, 而且在冒號後面添加 'Microsoft JhengHei,' : 




改好後按 Ctrl+O, 再按 Ctrl+X 存檔後跳出 nano.


3. 刪除 matplotlib 快取檔 (cache) : 

快取目錄位於 home 的 .cache 資料夾下, 必須用 rm -rf 指令刪除其中的 matplotlib 快取資料夾, 因為快取中存放的是之前純英文的字型, 如果不刪除的話, 執行 Matplotlib 繪圖程式時會直接去抓原本的快取, 必須刪除讓它重新建立快取才能抓到新增的中文字型檔 :

pi@raspberrypi:~ $ ls -ls .cache     
總計 28
4 drwx------ 3 pi pi 4096  4月  5  2019 chromium
4 drwxr-xr-x 2 pi pi 4096  4月  5  2019 fontconfig
4 drwxr-xr-x 3 pi pi 4096 11月 13  2018 lxsession
4 drwxr-xr-x 3 pi pi 4096  3月  5 08:01 matplotlib   
4 drwx------ 2 pi pi 4096  3月  5 17:17 menus
4 drwxr-xr-x 3 pi pi 4096  3月  5 17:17 openbox
4 drwx------ 4 pi pi 4096  4月  5  2019 pip
pi@raspberrypi:~/.cache $ rm -rf .cache/matplotlib   

經過上面三道程序後, 只要在程式中用 plt.rcParams() 指定微軟正黑體就可以在 Matplotlib 繪製的圖形中顯示中文了, 例如 :

import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"]=["Microsoft JhengHei"]    # 使用微軟正黑體
week=['日','一','二','三','四','五','六']
temperature=[25.4, 23.7, 28.6, 29.2, 24.8, 22.5, 21.9]
plt.plot(week, temperature)
plt.title('溫度變化圖')                      
plt.xlabel('星期')                                                 
plt.ylabel('攝氏')     
plt.show()

注意, 也可以用 plt.rcParams["font.family"]=["Microsoft JhengHei"], 結果如下 : 




Bingo! 終於搞定了, 以後我的自動機就能用 matplotlib 產生含有中文的圖形了. 

沒有留言 :