AIにアプリを作ってもらったお話

# はじめに
今回は試験勉強に集中するため、以前から興味のあったポモドーロタイマー（詳細は後述）を生成AIに作らせた。
コードをなんとなく読める程度の知識しかない私でも、生成AIの力を使うことで、驚くほど簡単にアプリを開発できたので、そのプロセスを紹介したいと思う。
### 【学生限定】Gemini AI Pro無料
なんと6月30日までの申込で、大学生限定ではあるが、Gemini AI Proに無料でアップグレードできるという。詳細は[こちら](https://gemini.google/students/?hl=ja)を確認してください。

月2900円の利益を得ることができますよ。

無料でGemini AI Proを使えることも、AIに作らせようと思ったきっかけのひとつ。
# 作ったもの：ポモドーロタイマー
今回作ったのはポモドーロタイマー。
25分と5分のタイマーを繰り返すもの。詳細は下の引用を見てください。
>## ポモドーロタイマーとは？
>ポモドーロタイマーは、集中力を高め生産性を向上させるための時間管理術、ポモドーロ・テクニックを実践する際に使用するタイマーです。このテクニックでは、25分間集中して作業し、その後に5分間の短い休憩を取るサイクルを繰り返します。この短い区切りが集中力の維持を助け、疲労を軽減することで、効率的な作業を可能にします。数サイクル繰り返した後には、15分から30分程度の長い休憩を挟むことで、リフレッシュ効果を高めます。（Generated by Gemini）
## 開発プロセス：PythonスクリプトからGUIアプリ、そしてインストーラーへ
とりあえず、「25分のタイマーを設定し、25分が経過したら5分のタイマーを設定し、5分が経過したらまた25分のタイマーを設定する（以下繰り返し）をしたいんだけど、何かアイデアはある？」とGeminiに尋ねてみた。
そうすると、Pythonを使ったコンソールベースのタイマーコードを提示してくれたので、Pythonで開発を進めることにした。
### ステップ1：コンソール版タイマーの作成
生成AIが最初に提案してくれたのが、以下のPythonスクリプト。
このコードは、コマンドライン上でタイマーのカウントダウンを表示し、時間が来るとビープ音で知らせてくれる。
```python
import time
import winsound
import os

def play_beep_sound():
    """簡単なビープ音を鳴らす"""
    winsound.Beep(1000, 500) # 周波数1000Hz、持続時間500ms

def start_timer(minutes, phase_name):
    """
    指定された時間タイマーを実行し、終了時に通知を鳴らす
    :param minutes: タイマーの時間（分）
    :param phase_name: 現在のフェーズ名（例: '作業', '休憩'）
    """
    print(f"--- {phase_name} ({minutes}分) タイマーを開始します ---")
    end_time = time.time() + minutes * 60

    while time.time() < end_time:
        remaining_seconds = int(end_time - time.time())
        mins, secs = divmod(remaining_seconds, 60)
        # カーソルを移動して表示を更新
        print(f"\r残り時間: {mins:02d}分{secs:02d}秒 ", end="", flush=True)
        time.sleep(1) # 1秒待機

    print("\n") # 表示行を確定させるために改行
    print(f"--- {phase_name}時間終了！ ---")
    play_beep_sound()
    print("次のフェーズに進むにはEnterキーを押してください...")
    input() # ユーザーがEnterを押すまで待機

def main():
    print("ポモドーロタイマーを開始します。")
    print("途中でCtrl+Cを押すと終了できます。")
    time.sleep(2) # 開始前の短い待機

    while True:
        try:
            start_timer(25, "作業")
            start_timer(5, "休憩")
        except KeyboardInterrupt:
            print("\nタイマーを終了します。")
            break
        except Exception as e:
            print(f"エラーが発生しました: {e}")
            break

if __name__ == "__main__":
    main()
```
このコードを実行してみると、要求通りに動作することを確認できた。
### ステップ2：GUIアプリ化への挑戦（Tkinter）
コンソール版の動作を確認した後、私は「アプリにしたい」と生成AIに伝えた。
すると、PythonのGUIライブラリであるTkinterを使ったGUIタイマーアプリのアイデアを提案してくれた。

提示されたコードは以下の通り。
ボタン操作でタイマーを開始・一時停止・リセットでき、視覚的に残り時間がわかるようになるなど、より使いやすいアプリになった。
```python
import tkinter as tk
from tkinter import messagebox
import time
import winsound
import threading

class PomodoroTimer:
    def __init__(self, master):
        self.master = master
        master.title("ポモドーロタイマー")
        master.geometry("400x250") # ウィンドウサイズを設定
        master.resizable(False, False) # ウィンドウサイズ変更を不可にする

        self.work_time = 25 * 60  # 25分 (秒単位)
        self.break_time = 5 * 60  # 5分 (秒単位)
        self.current_time = 0
        self.running = False
        self.is_work_phase = True # 現在が作業フェーズか休憩フェーズか

        # ウィジェットの作成
        self.label_phase = tk.Label(master, text="準備完了", font=("Helvetica", 20, "bold"), fg="blue")
        self.label_phase.pack(pady=10)

        self.label_timer = tk.Label(master, text="00:00", font=("Helvetica", 48, "bold"), fg="green")
        self.label_timer.pack(pady=10)

        # ボタンフレーム
        self.button_frame = tk.Frame(master)
        self.button_frame.pack(pady=10)

        self.btn_start_work = tk.Button(self.button_frame, text="作業開始 (25分)", command=self.start_work_timer, font=("Helvetica", 12))
        self.btn_start_work.pack(side=tk.LEFT, padx=5)

        self.btn_start_break = tk.Button(self.button_frame, text="休憩開始 (5分)", command=self.start_break_timer, font=("Helvetica", 12))
        self.btn_start_break.pack(side=tk.LEFT, padx=5)
        self.btn_start_break["state"] = "disabled" # 最初は休憩ボタンを無効化

        self.btn_pause = tk.Button(self.button_frame, text="一時停止", command=self.pause_timer, font=("Helvetica", 12), state="disabled")
        self.btn_pause.pack(side=tk.LEFT, padx=5)

        self.btn_reset = tk.Button(self.button_frame, text="リセット", command=self.reset_timer, font=("Helvetica", 12), state="disabled")
        self.btn_reset.pack(side=tk.LEFT, padx=5)

        self.update_timer_display(self.work_time) # 初期表示は作業時間

    def update_timer_display(self, seconds):
        """残り時間を表示を更新する"""
        mins, secs = divmod(int(seconds), 60)
        time_str = f"{mins:02d}:{secs:02d}"
        self.label_timer.config(text=time_str)

    def start_timer(self):
        """タイマーのカウントダウンを開始する"""
        if self.running:
            self.current_time -= 1
            self.update_timer_display(self.current_time)

            if self.current_time <= 0:
                self.running = False
                self.play_sound()
                if self.is_work_phase:
                    messagebox.showinfo("時間終了", "25分の作業時間が終了しました！休憩しましょう。")
                    self.label_phase.config(text="休憩準備完了", fg="green")
                    self.btn_start_break["state"] = "normal"
                    self.btn_start_work["state"] = "disabled"
                else:
                    messagebox.showinfo("時間終了", "5分の休憩時間が終了しました！作業に戻りましょう。")
                    self.label_phase.config(text="作業準備完了", fg="blue")
                    self.btn_start_work["state"] = "normal"
                    self.btn_start_break["state"] = "disabled"

                self.btn_pause["state"] = "disabled"
                self.btn_reset["state"] = "normal" # 終了時のみリセット可能にする
            else:
                self.master.after(1000, self.start_timer) # 1秒後に再度呼び出す

    def start_work_timer(self):
        """25分タイマーを開始する"""
        if not self.running:
            self.current_time = self.work_time
            self.is_work_phase = True
            self.running = True
            self.label_phase.config(text="作業中", fg="red")
            self.update_timer_display(self.current_time)
            self.start_timer()
            self.enable_buttons_on_start()

    def start_break_timer(self):
        """5分タイマーを開始する"""
        if not self.running:
            self.current_time = self.break_time
            self.is_work_phase = False
            self.running = True
            self.label_phase.config(text="休憩中", fg="darkgreen")
            self.update_timer_display(self.current_time)
            self.start_timer()
            self.enable_buttons_on_start()

    def pause_timer(self):
        """タイマーを一時停止/再開する"""
        if self.running:
            self.running = False
            self.btn_pause.config(text="再開")
        else:
            self.running = True
            self.btn_pause.config(text="一時停止")
            self.start_timer() # 再開時はカウントダウンを再開

    def reset_timer(self):
        """タイマーをリセットする"""
        self.running = False
        self.current_time = self.work_time # 次は作業時間からスタート
        self.is_work_phase = True
        self.update_timer_display(self.current_time)
        self.label_phase.config(text="準備完了", fg="blue")
        self.btn_start_work["state"] = "normal"
        self.btn_start_break["state"] = "disabled"
        self.btn_pause.config(text="一時停止", state="disabled")
        self.btn_reset["state"] = "disabled"

    def enable_buttons_on_start(self):
        """タイマー開始時のボタン状態を設定"""
        self.btn_start_work["state"] = "disabled"
        self.btn_start_break["state"] = "disabled"
        self.btn_pause["state"] = "normal"
        self.btn_reset["state"] = "normal"

    def play_sound(self):
        """時間終了時にビープ音を鳴らす"""
        # winsound.Beep(1000, 1000) # 周波数1000Hz、持続時間1000ms (1秒)
        # より目立つ音にする
        for _ in range(3): # 3回鳴らす
            winsound.Beep(2000, 300) # 高めの音
            time.sleep(0.1)
            winsound.Beep(1000, 300) # 低めの音
            time.sleep(0.1)

# アプリケーションの実行
if __name__ == "__main__":
    root = tk.Tk()
    app = PomodoroTimer(root)
    root.mainloop()
```
### ステップ3：実行ファイル（.exe）化とインストーラー作成
せっかくアプリにしたのだから、Python環境がないPCでも使えるようにしたいと考えた。
そこで、生成AIにpyinstallerを使った.exe化の方法を尋ねてみた。
指示通りに実行すると、問題なくWindowsで動作する実行ファイルを作成できた。

どうせならインストーラも作ってみようということで、その作成方法についても尋ねてみた。
Inno Setupというツールを提案され、インストール方法から教えてくれた。

Geminiが生成したコードはエラーを1度も起こすことなく、また、細かい修正を要求すると適切にコードを生成してくれた。
# おわりに
今回、プログラミング経験がほとんどない私でも、Geminiのおかげで簡単にアプリを作ることができた。

Geminiは単にコードを生成するだけでなく、多岐にわたる側面で開発をサポートしてくれた。

まず、必要な情報を一箇所で完結できた点は非常に便利だった。
通常、アプリ開発の知識がない場合、機能ごとに異なるWebサイトやドキュメントを検索し、情報を集める必要がある。
しかし、Geminiとの対話だけで、Pythonのコード生成から、GUI化のためのTkinter、実行ファイル化のためのPyInstaller、さらにはインストーラ作成のためのInno Setupといった、様々なツールやライブラリの具体的な使い方まで、複数のページを参照することなく一連のプロセスを進めることができた。

さらに、自分では明確に言語化できないことを雑に伝えても、意図を汲み取った提案をしてくれたので、迷うことなく作業を進められたことも便利に感じた。

大体2年前の話になるが、今回のポモドーロタイマーの開発とは別に、Discord Botの開発でChatGPTを使ったことがる。
当時は今ほど技術が発展していなかったということもあったからか、コードを生成するたびにエラー吐き、結局は人間主体で開発を進めることになった。
一方、今回の開発ではそういったエラーに悩まされることがなく、生成AIの技術がこの2年間で飛躍的に進化していることを強く感じた。

### ちなみに、この記事も生成AIを多用しています。
* * *
おそらくGeminiがかなり参考にしたであろう記事

https://qiita.com/Tadataka_Takahashi/items/556e0277017677cef68a


githubで配布しています。

https://github.com/ttettetsu/timerP/releases

![スクリーンショット 2025-06-14 224559.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516156/19e4a99b-abda-4c0b-a717-0ef55b316cb5.png)