4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonで作成!令和6年度共通テストのn進数タイマー

Last updated at Posted at 2024-09-19

はじめに

Screenshot 2024-09-19 at 15-30-27 - 2024_or_19_sugaku1A.pdf.png
 今回の記事では、Pythonを使って令和6年度共通テストのn進数タイマーを、デザインにも凝って要件定義、仕様策定、実装の3つに分けて解説します!これを実装するにあたり、@Tadataka_Takahashi様のPythonで作るポップなポモドーロタイマーを一部参考にして実装しました!

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仕様

スクリーンショット 2024-09-19 160250.png
 ・ウィンドウサイズ:1000x400

 ・表示要素:
  ◦タイトル
  ◦3つのタイマー
  ◦現在の時間

 ・操作要素:
  ◦開始
  ◦停止
  ◦リセット
  ◦設定

2.3設定仕様

 ・タイマーの進数を設定
 ・決定で変更

2.4設定UI仕様

スクリーンショット 2024-09-19 163109.png
 ・ウィンドウサイズ:450x300

 ・表示要素:
  ◦タイトル
  ◦3つのタイマーの進数の設定ラベル

 ・操作要素:
  ◦3つのタイマーの進数の設定テーブル
  ◦決定

2.5デザイン仕様

 ・カラーパレット
  ◦背景色:寒色(#87cefa)
  ◦メインカラー:鮮明な色(#4169e1)
  ◦アクセントカラー:補色となるような色(#ff7f50)
 ・フォント: 読みやすいサンセリフフォント(例:Helvetica)

3.実装

 仕様に基づき、Pythonコードを実装します。以下は実装例です。

main.py
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
スクリーンショット 2024-09-19 160250.png
 設定UI仕様
スクリーンショット 2024-09-19 163109.png
メインプログラム

main.py
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
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?