🔡
Tkinter でルビを表示する
Python の GUI ライブラリである Tkinter でルビを表示したい場面に遭遇しました。Web であれば簡単に表現可能なのですが、残念ながら Tkinter にルビ機能は存在しないため、スペースを用いて擬似的に表現します。
親文字用、ルビ用の TextLabel を用意する
親文字を表示する parentLabel
およびルビを表示する rubyLabel
を用意します。rubyLabel
のフォントサイズは parentLabel
の半分にセットします。
import tkinter as tk
size = 24
parentLabel = tk.Label(Display.root, text="", font=(FONT, size))
rubyLabel = tk.Label(Display.root, text="", font=(FONT, size / 2))
parentLabel.place(x=140, y=80)
rubyLabel.place(x=140, y=80 - 12)
ルビ付きテキストをパースする
今回は以下のようにルビを表記するものとします。モノルビ(1 文字ずつルビを振る)、グループルビ(単語にまとめてルビを振る)の場合も表記は同じです。
[明日:あした]からスペースを[用:もち]いて[擬:ぎ][似:じ][的:てき]に[表:ひょう][現:げん]
これをパースし、辞書を要素とする配列に格納します。
def parse_assignment_text(cls, text):
words = []
temp = { "parent": "", "ruby": None }
def add(temp):
if len(temp["parent"]) > 0:
words.append({ "parent": temp["parent"], "ruby": temp["ruby"] or "" })
temp["parent"] = ""
temp["ruby"] = None
for char in text:
if char == "[" or char == "]":
add(temp)
elif char == ":":
temp["ruby"] = ""
elif temp["ruby"] is not None:
temp["ruby"] += char
else:
temp["parent"] += char
add(temp)
return words
スペースを用いて表現する
あとは 1. で用意した parentLabel
, rubyLabel
にそれぞれ親文字、ルビを流し込んでいけばよいのですが、この際に、ルビが親文字の中央に収まるよう(中付き)に間隔を調整する必要があります。逆にルビが親文字をはみ出す場合は、親文字がルビの中央に収まるように配置します。この処理を図示すると、以下の通りになります。
Unicode には半角スペース(U+0020)や全角スペース(U+3000)のほかにも、以下の種類のスペースが存在しています。半角スペースは名称に反して 1/3 em(全角の 1/3 の幅)として表現されることが多いため、1/2 em や 1/4 em を表現するには不十分です。したがって今回は Four-Per-Em Space(U+2005)を用いて体裁を整えます。
Unicode | 名称 | 幅 |
---|---|---|
U+2004 | Three-Per-Em Space | 1/3 em、三分アキ |
U+2005 | Four-Per-Em Space | 1/4 em、四分アキ |
U+2006 | Six-Per-Em Space | 1/6 em、六分アキ |
スペーシングの処理を以下に示します。
import math
text = "[明日:あした]からスペースを[用:もち]いて[擬:ぎ][似:じ][的:てき]に[表:ひょう][現:げん]"
parsed = parse_assignment_text(text)
parent = ""
ruby = ""
for word in parsed:
parent_len = len(word["parent"])
ruby_len = len(word["ruby"])
parent_spaces = max((ruby_len - parent_len * 2) * 2, 0)
ruby_spaces = max((parent_len * 2 - ruby_len) * 4, 0)
parent += f"{" " * math.ceil(parent_spaces / 2)}{word["parent"]}{" " * math.floor(parent_spaces / 2)}"
ruby += f"{" " * math.ceil(ruby_spaces / 2)}{word["ruby"]}{" " * math.floor(ruby_spaces / 2)}"
parentLabel["text"] = parent
rubyLabel["text"] = ruby
コードを実行すると、以下のようにルビを表現できます。突出処理等は実装できていませんが、簡易的な実装として考えていただければ幸いです。
Discussion