はじめに
今回の記事では、Pythonを使って令和6年度共通テストのn進数タイマーを、デザインにも凝って要件定義、仕様策定、実装の3つに分けて解説します!これを実装するにあたり、@Tadataka_Takahashi様のPythonで作るポップなポモドーロタイマーを一部参考にして実装しました!
令和6年度共通テスト 数学I・A本試験問題はこちら!
令和6年度共通テスト 数学I・A本試験の解答はこちら!
1.要件定義
まず、n進数タイマーについて基本的な要件を定義します。
1.1機能要件
1.タイマーを3つ用意する。
2.スタートした時点では000と表示する。
3.タイマーがスタートした後、表示される数が1秒ごとに1ずつ増やす。
4.3桁で表示できる最大の数が表示された1秒後に、表示が000に戻る。
5.タイマーの表示が000に戻った後も、3~4を繰り返す。
6.タイマーの開始、停止、リセット、設定機能。
1.2非機能要件
1.ユーザーフレンドリーなインターフェース
2.視覚的にわかりやすいデザイン
3.操作が直感的で簡単であること
4.プログラムの安定性と信頼性
2.仕様策定
要件に基づき、具体的な仕様を決定します。
2.1タイマー仕様
・タイマーは1秒ごとにカウントアップ
・設定で決めた進数に応じて繰り上げ
2.2タイマーのUI仕様
・表示要素:
◦タイトル
◦3つのタイマー
◦現在の時間
・操作要素:
◦開始
◦停止
◦リセット
◦設定
2.3設定仕様
・タイマーの進数を設定
・決定で変更
2.4設定UI仕様
・表示要素:
◦タイトル
◦3つのタイマーの進数の設定ラベル
・操作要素:
◦3つのタイマーの進数の設定テーブル
◦決定
2.5デザイン仕様
・カラーパレット
◦背景色:寒色(#87cefa)
◦メインカラー:鮮明な色(#4169e1)
◦アクセントカラー:補色となるような色(#ff7f50)
・フォント: 読みやすいサンセリフフォント(例:Helvetica)
3.実装
仕様に基づき、Pythonコードを実装します。以下は実装例です。
import tkinter as tk
from tkinter import ttk, messagebox
class CountUpTimer:
def __init__(self):
self.window = tk.Tk()
self.window.title("n進数タイマー")
self.window.config(padx=20, pady=20, bg="#87cefa")
self.window.geometry("1000x400")
self.bases = [10, 10, 10] # デフォルトの進数(左から 10 10 10進数)
self.timers_running = False # タイマーのスタート初期値化
self.timer_values = [0, 0, 0] # タイマーの値の初期値化
self.timer_labels = []
self.setup_ui()
def setup_ui(self):
# フォントとカラーの設定
TITLE_FONT = ("Helvetica", 28, "bold")
TIMER_FONT = ("Helvetica", 64, "bold")
BUTTON_FONT = ("Helvetica", 14, "bold")
MAIN_COLOR = "#4169e1"
SECONDARY_COLOR = "#7fffd4"
ACCENT_COLOR = "#ff7f50"
self.title_label = tk.Label(text="n進数タイマー", fg=MAIN_COLOR, bg="#87cefa", font=TITLE_FONT)
self.title_label.pack()
# タイマーフレームのコンテナ
self.timer_frame_container = tk.Frame(self.window, bg="#87cefa")
self.timer_frame_container.pack(pady=20)
# タイマーフレームとラベル
for i in range(3):
frame = tk.Frame(self.timer_frame_container, bg="#0000cd", bd=2, relief="raised")
frame.pack(side=tk.LEFT, padx=10)
label = tk.Label(frame, text="000", fg=MAIN_COLOR, bg="#0000cd", font=TIMER_FONT, width=5) # Fixed width
label.pack(pady=20)
self.timer_labels.append(label)
# ボタン群のフレーム
self.button_frame = tk.Frame(self.window, bg="#87cefa")
self.button_frame.pack(pady=20)
# ボタンたち
self.start_button = tk.Button(self.button_frame, text="開始", padx=30, pady=10, command=self.start_timers, font=BUTTON_FONT, bg=SECONDARY_COLOR, width=10)
self.start_button.pack(side=tk.LEFT, padx=5)
self.stop_button = tk.Button(self.button_frame, text="停止", padx=30, pady=10, command=self.stop_timers, font=BUTTON_FONT, bg=SECONDARY_COLOR, width=10)
self.stop_button.pack(side=tk.LEFT, padx=5)
self.reset_button = tk.Button(self.button_frame, text="リセット", padx=30, pady=10, command=self.reset_timers, font=BUTTON_FONT, bg=SECONDARY_COLOR, width=10)
self.reset_button.pack(side=tk.LEFT, padx=5)
self.option_button = tk.Button(self.button_frame, text="設定", padx=30, pady=10, command=self.option_setup, font=BUTTON_FONT, bg=ACCENT_COLOR, width=10)
self.option_button.pack(side=tk.LEFT, padx=5)
def start_timers(self):
if not self.timers_running:
self.timers_running = True
self.update_timers()
def stop_timers(self):
self.timers_running = False
def reset_timers(self):
self.timers_running = False
self.timer_values = [0, 0, 0]
self.update_timer_labels()
def update_timers(self):
if self.timers_running:
for i in range(3):
self.timer_values[i] += 1
# 進数に基づいた繰り上げ
if self.timer_values[i] >= self.bases[i] ** 3:
self.timer_values[i] = 0
self.update_timer_labels()
self.window.after(1000, self.update_timers) # カウントアップ
def update_timer_labels(self):
for i, label in enumerate(self.timer_labels):
value = self.timer_values[i]
label.config(text=self.convert_to_base(value, self.bases[i]))
def convert_to_base(self, value, base):
chars = "0123456789ABCDEF"
result = ""
for _ in range(3):
result = chars[value % base] + result
value //= base
return result.zfill(3)
def option_setup(self):
# 新しいウィンドウの作成
settings_window = tk.Toplevel(self.window)
settings_window.title("設定")
settings_window.config(bg="#87cefa")
settings_window.geometry("450x300")
LABEL_FONT = ("Helvetica", 18)
BUTTON_FONT = ("Helvetica", 14, "bold")
ACCENT_COLOR = "#ff7f50"
label = tk.Label(settings_window, text="各タイマーの進数を設定してください。", font=LABEL_FONT, bg="#87cefa")
label.grid(row=0, column=0, columnspan=3, pady=10)
base_selectors = []
for i in range(3):
timer_label = tk.Label(settings_window, text=f"タイマー {i+1} の進数: ", font=LABEL_FONT, bg="#87cefa")
timer_label.grid(row=i + 1, column=0, padx=10, pady=10)
base_options = [str(n) for n in range(2, 17)] # Options from base 2 to 16
base_selector = ttk.Combobox(settings_window, values=base_options, font=("Helvetica", 14), width=5)
base_selector.set(str(self.bases[i])) # Set default value
base_selector.grid(row=i + 1, column=1, padx=10, pady=10)
base_selectors.append(base_selector)
apply_button = tk.Button(settings_window, text="決定", command=lambda: self.apply_settings(base_selectors, settings_window), font=BUTTON_FONT, bg=ACCENT_COLOR)
apply_button.grid(row=4, column=0, columnspan=3, pady=20)
def apply_settings(self, selectors, window):
try:
for i, selector in enumerate(selectors):
base = int(selector.get())
if base < 2 or base > 16:
raise ValueError
self.bases[i] = base
window.destroy()
except ValueError:
messagebox.showerror("入力エラー", "2から16の間の数値を入力してください。")
def run(self):
self.window.mainloop()
if __name__ == "__main__":
timer = CountUpTimer()
timer.run()
4.実装のポイント
n進数タイマーの実装について、以下のポイントに注意しました。
4.1クラスベースの設計
CountUpTimerクラスを中心に、アプリケーションの全機能をまとめ、再利用性と保守性を向上させた。
4.2UIデザインの考慮
色やフォントを適切に選択し、視覚的に魅力的なインターフェースを作成した。また、ボタンのサイズや配置を工夫し、使いやすさを向上させた。
4.3無効な入力の防止
無効な進数入力に対するエラーハンドリングを行い、プログラムの安全性を向上させた。
5.次回の展望
・問題が解けるように、任意のタイマーの組が同時に000となるような秒数の表示
・クロスプラットフォーム対応(Webアプリケーションやモバイルアプリへの展開)
次回には、誰でもこの問題について解けるようなアプリケーションに拡張していきたい。
6.まとめ
実際に要件定義をし、仕様策定をして実装する。といった作業をするのは今回が初めてでした。そのため、自身が気が付いていないバグが存在したり、不足の機能があると思います。ですが、完成までこぎつけることができたため、大変満足です!皆さんもPythonで日常の問題に対してアプリケーション化してみましょう!良いPythonライフを!
7.exe化したものの配布
exe化したものはこちら!手順は下記に記します。
・すべてダウンロード
・ファイルを展開
・distファイルを開く
・mainファイルを開く
・main.exeを起動
8.コメント対応:実装
実際に私自身は全然できない方です。そのため、指摘事項、改善の内容があるならば、コメント欄を参照ください。
メインUI
設定UI仕様
メインプログラム
import tkinter as tk
from tkinter import ttk, messagebox
class CountUpTimer:
def __init__(self):
self.window = tk.Tk()
self.window.title("n進数タイマー")
self.window.config(padx=20, pady=20, bg="#87cefa")
self.window.geometry("1000x400")
self.bases = [10, 10, 10] # デフォルトの進数(左から 10 10 10進数)
self.timers_running = False # タイマーのスタート初期値化
self.timer_values = [0, 0, 0] # タイマーの値の初期値化
self.timer_labels = []
self.setup_ui()
def setup_ui(self):
# フォントとカラーの設定
TITLE_FONT = ("Helvetica", 28, "bold")
TIMER_FONT = ("Helvetica", 64, "bold")
BUTTON_FONT = ("Helvetica", 14, "bold")
MAIN_COLOR = "#4169e1"
SECONDARY_COLOR = "#7fffd4"
ACCENT_COLOR = "#ff7f50"
self.title_label = tk.Label(text="n進数タイマー", fg=MAIN_COLOR, bg="#87cefa", font=TITLE_FONT)
self.title_label.pack()
# タイマーフレームのコンテナ
self.timer_frame_container = tk.Frame(self.window, bg="#87cefa")
self.timer_frame_container.pack(pady=20)
# タイマーフレームとラベル
for i in range(3):
frame = tk.Frame(self.timer_frame_container, bg="#0000cd", bd=2, relief="raised")
frame.pack(side=tk.LEFT, padx=10)
label = tk.Label(frame, text="000", fg=MAIN_COLOR, bg="#0000cd", font=TIMER_FONT, width=5) # Fixed width
label.pack(pady=20)
self.timer_labels.append(label)
# ボタン群のフレーム
self.button_frame = tk.Frame(self.window, bg="#87cefa")
self.button_frame.pack(pady=20)
# ボタンたち
self.start_button = tk.Button(self.button_frame, text="開始", padx=30, pady=10, command=self.start_timers, font=BUTTON_FONT, bg=SECONDARY_COLOR, width=10)
self.start_button.pack(side=tk.LEFT, padx=5)
self.stop_button = tk.Button(self.button_frame, text="停止", padx=30, pady=10, command=self.stop_timers, font=BUTTON_FONT, bg=SECONDARY_COLOR, width=10)
self.stop_button.pack(side=tk.LEFT, padx=5)
self.reset_button = tk.Button(self.button_frame, text="リセット", padx=30, pady=10, command=self.reset_timers, font=BUTTON_FONT, bg=SECONDARY_COLOR, width=10)
self.reset_button.pack(side=tk.LEFT, padx=5)
self.option_button = tk.Button(self.button_frame, text="設定", padx=30, pady=10, command=self.option_setup, font=BUTTON_FONT, bg=ACCENT_COLOR, width=10)
self.option_button.pack(side=tk.LEFT, padx=5)
def start_timers(self):
if not self.timers_running:
self.timers_running = True
self.update_timers()
def stop_timers(self):
self.timers_running = False
def reset_timers(self):
self.timers_running = False
self.timer_values = [0, 0, 0]
self.update_timer_labels()
def update_timers(self):
if self.timers_running:
for i in range(3):
self.timer_values[i] += 1
# 進数に基づいた繰り上げ
if self.timer_values[i] >= self.bases[i] ** 3:
self.timer_values[i] = 0
self.update_timer_labels()
self.window.after(1000, self.update_timers) # カウントアップ
def update_timer_labels(self):
for i, label in enumerate(self.timer_labels):
value = self.timer_values[i]
label.config(text=self.convert_to_base(value, self.bases[i]))
def convert_to_base(self, value, base):
chars = "0123456789ABCDEF"
result = ""
for _ in range(3):
result = chars[value % base] + result
value //= base
return result.zfill(3)
def option_setup(self):
# 新しいウィンドウの作成
settings_window = tk.Toplevel(self.window)
settings_window.title("設定")
settings_window.config(bg="#87cefa")
settings_window.geometry("450x300")
LABEL_FONT = ("Helvetica", 18)
BUTTON_FONT = ("Helvetica", 14, "bold")
ACCENT_COLOR = "#ff7f50"
label = tk.Label(settings_window, text="各タイマーの進数を設定してください。", font=LABEL_FONT, bg="#87cefa")
label.grid(row=0, column=0, columnspan=3, pady=10)
base_selectors = []
for i in range(3):
timer_label = tk.Label(settings_window, text=f"タイマー {i+1} の進数: ", font=LABEL_FONT, bg="#87cefa")
timer_label.grid(row=i + 1, column=0, padx=10, pady=10)
base_options = [str(n) for n in range(2, 17)] # Options from base 2 to 16
base_selector = ttk.Combobox(settings_window, values=base_options, font=("Helvetica", 14), width=5)
base_selector.set(str(self.bases[i])) # Set default value
base_selector.grid(row=i + 1, column=1, padx=10, pady=10)
base_selectors.append(base_selector)
apply_button = tk.Button(settings_window, text="決定", command=lambda: self.apply_settings(base_selectors, settings_window), font=BUTTON_FONT, bg=ACCENT_COLOR)
apply_button.grid(row=4, column=0, columnspan=3, pady=20)
def apply_settings(self, selectors, window):
try:
for i, selector in enumerate(selectors):
base = int(selector.get())
if base < 2 or base > 16:
raise ValueError
self.bases[i] = base
window.destroy()
except ValueError:
messagebox.showerror("入力エラー", "2から16の間の数値を入力してください。")
def run(self):
self.window.mainloop()
if __name__ == "__main__":
timer = CountUpTimer()
timer.run()