以下は『影に潜む銘文にて鍵盤は赤熱す』のソースコード全文です:
import tkinter as tk
from tkinter import ttk
from collections import Counter
import colorsys
# キーボード配列
KEYBOARD_LAYOUTS = {
'ja/en': [
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'],
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'],
['z', 'x', 'c', 'v', 'b', 'n', 'm'],
],
'ita': [
['', '', '', '', '', '', '', '', '', '', '', 'ì'],
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'è'],
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ò', 'à', 'ù'],
['z', 'x', 'c', 'v', 'b', 'n', 'm'],
],
'esp': [
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'],
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ñ', '', 'ç'],
['z', 'x', 'c', 'v', 'b', 'n', 'm'],
],
'fra': [
['', 'é', '', '', '', '', 'è', '', 'ç', 'à', ''],
['a', 'z', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'],
['q', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'ù'],
['w', 'x', 'c', 'v', 'b', 'n']
],
'deu': [
['', '', '', '', '', '', '', '', '', '', 'ß'],
['q', 'w', 'e', 'r', 't', 'z', 'u', 'i', 'o', 'p', 'ü'],
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ö', 'ä'],
['y', 'x', 'c', 'v', 'b', 'n', 'm']
],
'rus': [
['ё'],
['й', 'ц', 'у', 'к', 'е', 'н', 'г', 'ш', 'щ', 'з', 'х', 'ъ'],
['ф', 'ы', 'в', 'а', 'п', 'р', 'о', 'л', 'д', 'ж', 'э'],
['я', 'ч', 'с', 'м', 'и', 'т', 'ь', 'б', 'ю']
]
}
VALID_CHARS = set("abcdefghijklmnopqrstuvwxyzìèòàùñçéèçàùßäüöабвгдеёжзийклмнопрстуфхцчшщъыьэюя")
def frequency_to_color(freq, max_freq):
ratio = freq / max_freq if max_freq else 0
hue = (1 - ratio) * 0.75
r, g, b = colorsys.hsv_to_rgb(hue, 1.0, 1.0)
return f'#{int(r*255):02x}{int(g*255):02x}{int(b*255):02x}'
def count_chars(text):
return Counter(c.lower() for c in text if c.lower() in VALID_CHARS)
class HeatmapKeyboardApp:
def __init__(self, root):
self.root = root
self.root.title("キーボード・ヒートマップ - 影に潜む銘文にて鍵盤は赤熱す")
self.text_area = tk.Text(root, height=10, font=("Arial", 12))
self.text_area.pack(fill=tk.BOTH, padx=10, pady=(10, 0))
self.lang_var = tk.StringVar(value='ja/en')
lang_menu = ttk.OptionMenu(root, self.lang_var, 'ja/en', *KEYBOARD_LAYOUTS.keys(), command=self.update_keyboard)
lang_menu.pack(pady=5)
self.bottom_frame = tk.Frame(root)
self.bottom_frame.pack(pady=10, padx=10, fill=tk.X)
self.keyboard_frame = tk.Frame(self.bottom_frame)
self.keyboard_frame.pack(side=tk.LEFT)
self.legend_canvas = tk.Canvas(self.bottom_frame, width=30, height=120)
self.legend_canvas.pack(side=tk.LEFT, padx=10)
update_btn = ttk.Button(root, text="ヒートマップ更新", command=self.update_keyboard)
update_btn.pack(pady=(0, 10))
romaji_btn = ttk.Button(root, text="日本語をローマ字へ", command=self.open_romaji_converter)
romaji_btn.pack(pady=(0, 10))
self.update_keyboard()
def update_keyboard(self, *args):
for widget in self.keyboard_frame.winfo_children():
widget.destroy()
self.legend_canvas.delete("all")
text = self.text_area.get("1.0", tk.END)
freq = count_chars(text)
max_freq = max(freq.values(), default=1)
layout = KEYBOARD_LAYOUTS[self.lang_var.get()]
for row_idx, row in enumerate(layout):
for col_idx, key in enumerate(row):
key_disp = "Space" if key == ' ' else key.upper()
f = freq.get(key.lower(), 0)
if f == 0:
color = '#000000'
fg_color = '#FFFFFF'
else:
color = frequency_to_color(f, max_freq)
fg_color = '#FFFFFF'
label = tk.Label(self.keyboard_frame, text=key_disp,
bg=color, fg=fg_color, width=5, height=2,
font=("Helvetica", 12, "bold"), relief='ridge', borderwidth=2)
label.grid(row=row_idx, column=col_idx, padx=3, pady=3)
self.draw_legend()
def draw_legend(self):
steps = 10
for i in range(steps):
freq = (steps - i)
color = frequency_to_color(freq, steps)
y0 = i * 12
y1 = y0 + 12
self.legend_canvas.create_rectangle(0, y0, 30, y1, fill=color, outline="")
self.legend_canvas.create_text(15, 0, anchor='n', text="多", fill="white", font=("Arial", 9, "bold"))
self.legend_canvas.create_text(15, 120, anchor='s', text="少", fill="white", font=("Arial", 9, "bold"))
def open_romaji_converter(self):
def convert_to_romaji():
from pykakasi import kakasi
kks = kakasi()
input_text = input_box.get("1.0", tk.END)
result = kks.convert(input_text)
output_text = "".join([item['hepburn'] for item in result])
output_box.delete("1.0", tk.END)
output_box.insert(tk.END, output_text)
win = tk.Toplevel(self.root)
win.title("日本語 → ローマ字")
tk.Label(win, text="日本語入力").pack(pady=(10, 0))
input_box = tk.Text(win, height=5, font=("Arial", 12))
input_box.pack(fill=tk.BOTH, padx=10, pady=5)
convert_btn = ttk.Button(win, text="変換", command=convert_to_romaji)
convert_btn.pack(pady=5)
tk.Label(win, text="ローマ字出力").pack()
output_box = tk.Text(win, height=5, font=("Arial", 12))
output_box.pack(fill=tk.BOTH, padx=10, pady=(5, 10))
if __name__ == "__main__":
root = tk.Tk()
app = HeatmapKeyboardApp(root)
root.mainloop()