ランダム名言ジェネレーター
ボタンを押すたびにランダムな名言を表示するアプリ。リストからの無作為選択と表示更新を学びます。
1. アプリ概要
ボタンを押すたびにランダムな名言を表示するアプリ。リストからの無作為選択と表示更新を学びます。
このアプリはツールカテゴリに分類される実践的なGUIアプリです。使用ライブラリは tkinter(標準ライブラリ) で、難易度は ★☆☆ です。
このアプリは「ツール」カテゴリです。日々の作業を自動化する実用ツールは Python が最も得意とする領域です。短いコードで実用性のある成果物が作れる点が魅力です。tkinter(標準ライブラリ) を活かして実装するこの構造は、他のアプリにも応用が効きます。
動かしながら読むことが理解の最短経路です。まずはコードをコピーして実行し、想定どおりに動くことを確認したうえで解説と照らし合わせてください。
カスタマイズでは「機能追加」「UI 改善」「エラー耐性」の三方向で考えると視野が広がります。練習問題にもそれぞれの方向の具体例を用意しています。
2. 機能一覧
- 「🔀 次の名言」ボタン(
show_quote())・「⭐ お気に入り」ボタン(add_favorite())・「📋 コピー」ボタン(copy_quote())で操作 tk.Listboxによる一覧表示- JSON ファイルの保存・読み込み(起動時に自動実行)
messagebox.showerror()・messagebox.showwarning()によるダイアログ通知- ウィンドウはタイトル「ランダム名言ジェネレーター」・サイズ 520x416 で起動
- お気に入りは
favorites.jsonに保存され、次に起動したときに自動で読み込まれる(保存先は app44.py と同じフォルダ)
3. 事前準備・環境
Python 3.10 以上 / Windows 11(実機)で起動・基本操作を確認 / Linux Mint 22.3(仮想マシン)で起動・画面表示を確認(macOS は未検証)
Windows 11(実機)で起動と基本操作を確認しています(全機能の網羅テストではありません)。Linux Mint 22.3(仮想マシン)では起動と画面表示を確認しました。macOS は標準ライブラリの範囲で動作する想定ですが、未検証です。
- Python 3.10 以上
- OS: Windows 11(実機で起動・基本操作を確認)・Linux Mint 22.3(仮想マシンで起動・画面表示を確認)
4. 完全なソースコード
右上の「コピー」ボタンをクリックするとコードをクリップボードにコピーできます。
import tkinter as tk
from tkinter import ttk, messagebox
import json
import os
import random
# お気に入りの保存先。このスクリプトと同じフォルダに favorites.json が作られる
FAV_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "favorites.json")
class App44:
"""ランダム名言ジェネレーター"""
QUOTES = [
("シンプルさは究極の洗練である。", "レオナルド・ダ・ヴィンチ"),
("プログラミングはアートであり、科学であり、工学でもある。", "ドナルド・クヌース"),
("コードの読みやすさは、書きやすさと同じくらい重要だ。", "グイド・ヴァンロッサム"),
("最初に動くものを作れ。それから美しくせよ。", "ケント・ベック"),
("バグを見つけるよりも、バグを作らないことの方が難しい。", "エドガー・ダイクストラ"),
("学ぶことをやめたら、教えることもやめなければならない。", "アルベルト・アインシュタイン"),
("失敗は成功のもと。大切なのは諦めないこと。", "トーマス・エジソン"),
("千里の道も一歩から。", "老子"),
("コードは書く回数より読む回数の方がはるかに多い。", "不明"),
("動くコードは最良のドキュメントだ。", "不明"),
("シンプルにすることは複雑にするよりも難しい。", "スティーブ・ジョブズ"),
("最良のエラーメッセージとは、エラーが起きないことだ。", "不明"),
("まず動かせ。正しくせよ。速くせよ。", "肯特・ベック"),
("コードを書く前に考え、考える前に仕様を理解せよ。", "不明"),
("チームワークは夢を現実にする。", "不明"),
]
def __init__(self, root):
self.root = root
self.root.title("ランダム名言ジェネレーター")
self.root.geometry("520x416")
self.root.minsize(520, 416)
self.root.configure(bg="#f8f9fc")
self.favorites = []
self._build_ui()
self._load_favorites()
self.show_quote()
def _build_ui(self):
title_frame = tk.Frame(self.root, bg="#3776ab", pady=12)
title_frame.pack(fill=tk.X)
tk.Label(title_frame, text="ランダム名言ジェネレーター",
font=("Noto Sans JP", 16, "bold"),
bg="#3776ab", fg="white").pack()
main_frame = tk.Frame(self.root, bg="#f8f9fc", padx=24, pady=16)
main_frame.pack(fill=tk.BOTH, expand=True)
# 名言カード
card = tk.Frame(main_frame, bg="white", relief=tk.RAISED, bd=1)
card.pack(fill=tk.X, pady=(0, 14), ipady=16)
tk.Label(card, text="❝", font=("Noto Sans JP", 24), bg="white",
fg="#3776ab").pack(anchor="w", padx=16, pady=(8, 0))
self.quote_label = tk.Label(card, text="", bg="white",
font=("Noto Sans JP", 13),
wraplength=440, justify="center",
fg="#222")
self.quote_label.pack(padx=16, pady=(0, 8))
self.author_label = tk.Label(card, text="", bg="white",
font=("Noto Sans JP", 11, "italic"),
fg="#888")
self.author_label.pack(anchor="e", padx=16, pady=(0, 8))
# ボタン
btn_frame = tk.Frame(main_frame, bg="#f8f9fc")
btn_frame.pack()
tk.Button(btn_frame, text="🔀 次の名言",
font=("Noto Sans JP", 13, "bold"),
bg="#3776ab", fg="white", activebackground="#2a5a8a",
relief=tk.FLAT, padx=16, pady=8,
command=self.show_quote).pack(side=tk.LEFT, padx=6)
ttk.Button(btn_frame, text="⭐ お気に入り", command=self.add_favorite).pack(side=tk.LEFT, padx=6)
ttk.Button(btn_frame, text="📋 コピー", command=self.copy_quote).pack(side=tk.LEFT, padx=6)
# お気に入り
fav_frame = ttk.LabelFrame(main_frame, text="お気に入り", padding=6)
fav_frame.pack(fill=tk.BOTH, expand=True, pady=(14, 0))
self.fav_listbox = tk.Listbox(fav_frame, font=("Noto Sans JP", 9),
bg="white", relief=tk.FLAT, height=4)
self.fav_listbox.pack(fill=tk.BOTH, expand=True)
self.current_quote = None
def show_quote(self):
quote, author = random.choice(self.QUOTES)
self.current_quote = (quote, author)
self.quote_label.config(text=quote)
self.author_label.config(text=f"— {author}")
def add_favorite(self):
if self.current_quote:
q, a = self.current_quote
entry = f"{q}({a})"
if entry not in self.favorites:
self.favorites.append(entry)
self.fav_listbox.insert(tk.END, entry)
self._save_favorites()
def copy_quote(self):
if self.current_quote:
q, a = self.current_quote
self.root.clipboard_clear()
self.root.clipboard_append(f"「{q}」— {a}")
def _load_favorites(self):
"""保存済みのお気に入りを読み込む(無い・壊れている場合は空のまま続行)"""
try:
with open(FAV_FILE, "r", encoding="utf-8") as f:
saved = json.load(f)
except FileNotFoundError:
return
except (OSError, json.JSONDecodeError):
messagebox.showwarning(
"お気に入り",
f"{FAV_FILE} を読み込めませんでした。お気に入りは空の状態で始めます。")
return
if not isinstance(saved, list):
return
for entry in saved:
if isinstance(entry, str) and entry not in self.favorites:
self.favorites.append(entry)
self.fav_listbox.insert(tk.END, entry)
def _save_favorites(self):
"""お気に入りを JSON ファイルへ保存する"""
try:
with open(FAV_FILE, "w", encoding="utf-8") as f:
json.dump(self.favorites, f, ensure_ascii=False, indent=2)
except OSError as e:
messagebox.showerror("保存エラー", f"お気に入りを保存できませんでした: {e}")
if __name__ == "__main__":
root = tk.Tk()
app = App44(root)
root.mainloop()
5. コード解説
ランダム名言ジェネレーターのコードを、実際に書かれている実装に沿って解説します。
クラス設計とコンストラクタ
App44 クラスにアプリの全機能をまとめています(メソッド7個・全143行)。__init__ ではタイトル「ランダム名言ジェネレーター」とウィンドウサイズ 520x416 を設定します。そのあと _build_ui() → _load_favorites() → show_quote() の順に呼び出して初期状態を作ります。
※ 該当部分のコード本体は 「4. 完全なソースコード」 をご参照ください(重複表示を避けるため再掲を省略しています)。
ウィジェット構成
画面は tk.Frame×4・tk.Label×4・tk.Button・ttk.Button×2・ttk.LabelFrame・tk.Listbox で構成しています。見出し付きの枠(LabelFrame)は「お気に入り」のラベルでエリアを区切っています。
※ 該当部分のコード本体は 「4. 完全なソースコード」 をご参照ください(重複表示を避けるため再掲を省略しています)。
イベント処理とボタンの接続
「🔀 次の名言」ボタン(show_quote())・「⭐ お気に入り」ボタン(add_favorite())・「📋 コピー」ボタン(copy_quote())を command= で接続しています。
※ 該当部分のコード本体は 「4. 完全なソースコード」 をご参照ください(重複表示を避けるため再掲を省略しています)。
JSON への保存と読み込み
データは json.dump() でファイル(favorites.json)へ保存します。起動時は json.load() で読み戻します。
※ 該当部分のコード本体は 「4. 完全なソースコード」 をご参照ください(重複表示を避けるため再掲を省略しています)。
例外処理
try-except で FileNotFoundError・OSError・json.JSONDecodeError を捕捉しています。あわせて messagebox.showerror()・messagebox.showwarning() のダイアログで、ユーザーへの通知・確認を行います。
※ 該当部分のコード本体は 「4. 完全なソースコード」 をご参照ください(重複表示を避けるため再掲を省略しています)。
お気に入りが次の起動でも残る仕組み
保存の流れは3ステップです。open() でファイルを開く、json.dump() で書き出す(読むときは json.load())、with ブロックを抜けたところで自動的に閉じる。書き出すのは「⭐ お気に入り」を押したとき(add_favorite() の末尾)だけです。
読み込み側は失敗しても止まらないように書いています。ファイルがまだ無い初回起動は FileNotFoundError をそのまま無視して空の一覧で始め、中身が壊れて JSON として読めないときは json.JSONDecodeError、権限などで開けないときは OSError を捕まえ、警告ダイアログを出したうえで空の一覧に戻します。except Exception でまとめて握りつぶさず、想定した3種類だけを捕まえるのが安全な書き方です。
保存先は os.path.dirname(os.path.abspath(__file__)) でスクリプト自身のフォルダを求めて組み立てています。ターミナルを開いている場所(カレントディレクトリ)ではないため、どこから python app44.py を実行しても同じ favorites.json を読み書きします。
6. ステップバイステップガイド
このアプリをゼロから自分で作る手順を解説します。コードをコピーするだけでなく、実際に手順を追って自分で書いてみましょう。
-
1ファイルを作成する
新しいファイルを作成して app44.py と保存します。使うのは
json・os・random・tkinterだけなので、追加インストールは不要です。 -
2クラスの骨格を作る
App44クラスを定義し、__init__と、末尾のroot = tk.Tk()~mainloop()の最小構成を書いて、まず空のウィンドウが出ることを確認します。 -
3ウィンドウを設定する
title("ランダム名言ジェネレーター")・geometry("520x416")・configure(bg="#f8f9fc")・minsize(520, 416)をコンストラクタで設定します。 -
4画面部品を並べる
_build_ui()の中で、tk.Frame×4・tk.Label×4・tk.Button・ttk.Button×2・ttk.LabelFrame・tk.Listboxを作って配置します(掲載コードと同じ並び順で書くとレイアウトが一致します)。まず表示だけ確認しましょう。 -
5イベントを接続する
「2. 機能一覧」で挙げた各ボタンを
command=で対応するメソッド(show_quote()・add_favorite()・copy_quote())につなぎます。 -
6中心になるメソッドを実装する
アプリの本体である
add_favorite()(8行)・show_quote()(5行)・copy_quote()(5行) を実装します。 -
7動作確認する
python app44.pyで起動し、各ボタンが反応することを確認します。
7. カスタマイズアイデア
基本機能を習得したら、以下のカスタマイズに挑戦してみましょう。少しずつ機能を追加することで、Pythonのスキルが飛躍的に向上します。
💡 ダークモードを追加する
bg色・fg色を辞書で管理し、ボタン1つでダークモード・ライトモードを切り替えられるようにしましょう。
💡 クラス定数を書き換えて動きを変える
データはクラス定数(QUOTES)にまとまっています。中身を書き換えて動きが変わることを確かめましょう。複数の定数が対応関係にある場合は、セットで整合させる必要がある点に注意してください。
💡 配色をまとめて管理する
背景色 #f8f9fc をはじめ、色コードが各所に直書きされています。色を1つの辞書にまとめると、テーマの切り替えが1か所の変更で済むようになります。
💡 お気に入りを選んで削除できるようにする
いまの一覧は追加専用で、間違えて登録した名言を消せません。Listbox の curselection() で選択行の位置を取り、self.favorites から del し、Listbox.delete() で画面からも消して、最後に _save_favorites() を呼びます。この順番で書くと、画面の表示と favorites.json の中身がずれません。
💡 背景色とフォントをランダムに変える
名言を切り替えるたびに見た目も変わると、めくる楽しさが出ます。背景色の候補とフォント名の候補をそれぞれリストで持ち、show_quote() の中で random.choice() で選んで、名言カードと quote_label に config(bg=...)・config(font=...) で反映します。文字が読めない組み合わせにならないよう、候補は明るい背景色だけに絞るのがコツです。
8. よくある問題と解決法
❌ 文字のフォントが崩れる・見た目が違う
原因:このコードは font=("Noto Sans JP", ...) のようにフォント名を直接指定しています。環境にこのフォントが入っていないと、OS が代わりのフォントで表示するため、見本と見た目が変わることがあります。
解決法:動作には支障ありません。気になる場合は font 引数の名前を自分の OS に入っているフォントへ書き換えるか、font 引数を省略してください。
❌ ウィンドウの大きさが画面に合わない
原因:geometry("520x416") の固定サイズで起動するためです。
解決法:geometry() と minsize() の両方の数値を書き換えて起動サイズを調整してください。なお、このコードにはウィンドウサイズを固定する設定(resizable の指定)が無いため、ウィンドウの端をドラッグしたサイズ変更は既定どおり可能です。ただし minsize(520, 416) を指定しているため、起動時の大きさより小さくは縮められません(縮めると画面部品が表示されなくなるのを防ぐためです)。
❌ お気に入りが次に開くと消えている
原因:お気に入りをリスト変数(self.favorites)にだけ持っていると、ウィンドウを閉じた時点でメモリから消えます。ファイルへ書き出していなければ、次の起動で復元する手段がありません。
解決法:このコードでは add_favorite() のたびに _save_favorites() で favorites.json へ書き出し、起動時に _load_favorites() で読み戻しています。消えている場合は、app44.py と同じフォルダに favorites.json が作られているかを確認してください。無い場合は書き込み権限のないフォルダにスクリプトを置いている可能性があり、そのときは「保存エラー」のダイアログが出ます。
9. 練習問題
アプリの理解を深めるための練習問題です。気になるものから挑戦してみてください(課題3は定型の発展課題です)。QUOTES(14〜30行)は学習用に短くまとめた文で、作者名まで含めて出典を保証するものではありません。本や歌詞、記事の文章を書き足すときは著作権に注意し、自分の手元で試す範囲にとどめてください。「⭐ お気に入り」を押すと、app44.py と同じフォルダに favorites.json が作られます(8行・131〜137行)。
-
課題1:直前と同じ名言が続けて出ないようにする
「🔀 次の名言」を押すと、
show_quote()がrandom.choice()で15件から1つ選びます(91〜95行)。前に出した名言を避ける処理はありません。毎回15件から選び直すので、同じ名言が2回続く確率は約6.7%(15回に1回)です。期待結果:「🔀 次の名言」を何度押しても、直前と同じ名言が2回続けて出ない
合格条件
- 押すたびに表示される名言が変わる
- 選ばれた名言は
self.current_quote(93行)に入り、「⭐ お気に入り」と「📋 コピー」がこれまでどおり動く - 起動直後の1回目(41行)でもエラーにならない
ヒント①(どこを触るか): 触るのは1か所です。
show_quote()の92行、random.choice()で選んでいるところです。
ヒント②(使うもの):whileを使い、選んだ組がself.current_quoteと同じあいだは選び直します。self.current_quoteは89行でNoneから始まるため、1回目は1度で抜けられます。
つまずきやすい点: 名言を1件だけの構成に書き換えると、このwhileは永久に終わりません。len(self.QUOTES) > 1を先に確かめると安全です。random.sample(self.QUOTES, 2)で2件取り出す書き方もありますが、これも1件だとValueErrorになります。 -
課題2:「⭐ お気に入り」を押した結果を画面で知らせる
「⭐ お気に入り」を押しても、画面には何も出ません(97〜104行)。すでに登録済みの名言なら一覧も増えないので、ボタンが効いたのか壊れているのかを区別できません。登録できたかどうかは、下のお気に入り一覧(85〜87行)が増えたかどうかで判断するしかありません。
期待結果:同じ名言で「⭐ お気に入り」を2回押すと、1回目は「お気に入りに追加しました」、2回目は「すでにお気に入りにあります」と出る
合格条件
- 1回目のあと、お気に入り一覧(85〜87行)が1行だけ増える
- 2回目は一覧が増えない(101行の重複チェックはそのまま残す)
favorites.jsonにも1件ぶんだけ増える
ヒント①(どこを触るか): 触るのは2か所です。追加できたとき=104行の
_save_favorites()の下と、すでにあったとき=101行のifに対するelseです。
ヒント②(使うもの): どちらもmessagebox.showinfo("お気に入り", "…")の1行で足ります。messageboxは2行目で読み込み済みで、120〜122行に同じ書き方があります。
つまずきやすい点: 知らせを101行より前へ置くと、追加できていないのに「追加しました」と出ます。104行の_save_favorites()は保存に失敗すると自分でエラーを出すので(136〜137行)、その前に「追加しました」と出すと知らせが2つ並びます。順番は「一覧に足す → 保存する → 知らせる」です。 -
課題3:キー操作に対応させる
ボタンから呼んでいる
show_quote()を、キーボードのキーからも呼べるようにしてみましょう。tkinter では bind を使ってキーイベントとメソッドを結び付けられます。
写経中に赤いエラー文が出たら、Pythonエラー一覧&解決法(英語メッセージ逆引き)で原因と直し方をすぐ確認できます。
写経しながら「なぜこう書くのか」が気になり始めたら、入門書を1冊通して読むと断片的な知識がつながります。Python入門書のおすすめ2冊(当サイトの参考書ランキング総合1〜2位)で、独学者向けの最初の1冊を比較しています。