🟢 どんなアプリを作ったのか?
「映画とかアニメで出てくる、あの“カウントダウンUI”ってかっこよくないですか?」
そんな気持ちから、某アニメ風のカウントダウンタイマーアプリをPython(PyQt)で作ってみました。
実際に作ったアプリのスクショがこちら👇

デザインのテーマは「赤 × 黒 × 未来感」+「文字は大きく&中央にドーンと配置」。
単なるストップウォッチやタイマーではなく、
**“見ていて楽しい、演出のあるタイマー”**を目指しました。
ちなみにこのタイマーアプリを作ろうと思ったきっかけは、すごくシンプルです。
- 普段、**ポモドーロタイマー(25分作業+5分休憩)**を使って勉強していた
- でも、だんだんと「自分専用のタイマーが欲しいな」と思うようになった
市販のアプリでも十分便利なんですが、
「せっかくなら見た目も好きなデザインで、自分で作ってみたい!」と思い立って、
今回の制作に踏み切りました。
🔸 主なポイントは以下の3つ:
- 背景に画像を使ってUI全体を演出
- 時間表示は縦長フォントで存在感UP
- 残り10秒で点滅エフェクト、さらに起動時にも軽い演出あり!
日常での実用性というよりは、
**「ちょっとテンションの上がる演出タイマー」**という感覚で、遊び心を大切にしました。
🟡 アプリの仕様まとめ
ここでは、今回作ったタイマーアプリの機能・デザイン・こだわりポイントをざっくりまとめておきます。
🔧 アプリの基本仕様
| 項目 | 内容 |
|---|---|
| 機能 | カウントダウンタイマー(スタート/ストップ/リセット) |
| UI構成 | 背景画像+数字ラベル+操作ボタン(スタート・リセット) |
| タイマー動作 | 秒単位でカウント、0秒になると自動停止 |
| プリセット | なし(時間は手動入力 or コードで固定) |
🎨 デザインのこだわり
- 背景に自作 or フリー画像を敷いて、UIの世界観を強調
- 数字フォントは縦長・太字の読みやすいスタイルを選択
- 配色は赤×黒をベースに、エッジの効いた印象に
✨ 追加の演出・機能
- 起動時にフェードイン演出を追加して、テンションを上げる
- 残り10秒になると、数字が赤く点滅して“緊張感”を演出
🧰 使用技術・ライブラリ
| 使用技術 | 用途 |
|---|---|
| Python | メイン言語 |
| PyQt5 | GUI開発(ウィンドウ、ボタン、ラベルなど) |
| QTimer | 時間制御・点滅処理 |
| QPropertyAnimation | アニメーション処理(フェードインなど) |
こうやって書き出してみると、機能はシンプルですが、
見た目・演出・世界観のバランスを取るのが意外と楽しい&難しい部分でした。
🟠 制作ステップ(ざっくり解説)
ここからは、タイマーアプリを作るまでの流れをざっくりと紹介します。
🔹① UIデザインからスタート
まずはPyQtのデザイナ感覚で、ウィンドウに要素を配置していくところから始めました。
- 背景画像を設定し、ウィンドウ全体に貼り付け
- 中央に大きなカウントダウン用の数字ラベルを配置
- 下部にスタート/リセット用ボタンを設置
全体的に“未来っぽさ”と“非日常感”が出るように、
配置や余白にも気をつけました。
画像:UI初期デザイン
🔹② タイマーのロジックを実装
次に、PyQtのQTimerを使って、カウントダウンのロジックを組みました。
- 秒単位で時間を減らして、画面上に表示
- カウントが0になったらタイマー停止
- リセットボタンで時間を初期値に戻す
特に「残り時間の表示更新」は、QTimerとの連携がカギになります。
pythonコピーする編集するself.timer = QTimer()
self.timer.timeout.connect(self.update_time)
self.timer.start(60) # 60msごと更新。これ以上早くすると処理が追い付かない
🔹③ アニメーション・演出の追加
基本の動作ができたら、最後は**“ちょっとカッコよく”演出を追加**!
- アプリ起動時にフェードイン(透明 → 表示)
- 0秒になったら数字が黄色く点滅

PyQtのQPropertyAnimationで透明度や色の変化を制御しました。 GUIだけど動きがあるだけで、一気に“作品感”が出ます。
全体として、**「見た目 × 機能 × 演出」**を一体に作り込むのが今回の目標でした。
初めてやってみると、PyQtでも意外と色々できるなって感じました!
🟤 今回作成したコード(完成形)
作成したコード(クリック)
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QGraphicsOpacityEffect, QLineEdit
from PyQt5.QtGui import QPixmap, QFont
from PyQt5.QtCore import Qt, QTimer
import sys
class BaseScreen(QWidget):
def set_background(self, image_path):
self.bg_label = QLabel(self)
self.bg_label.setPixmap(QPixmap(image_path))
self.bg_label.setGeometry(0, 0, 800, 400)
self.bg_label.setScaledContents(True)
def create_button(self, text, x, y, width, height, callback, border_color="red"):
btn = QPushButton(text, self)
btn.setGeometry(x, y, width, height)
btn.setStyleSheet(f"background: transparent; border: 2px solid {border_color}; color: white; font-size: 20px;")
btn.clicked.connect(callback)
return btn
class StartScreen(BaseScreen):
def __init__(self, switch_callback):
super().__init__()
self.switch_callback = switch_callback
self.initUI()
def initUI(self):
self.setWindowTitle("EVA Timer - Start")
self.setGeometry(100, 100, 800, 400)
self.set_background("Start画面.png")
# フェードインエフェクト
self.opacity_effect = QGraphicsOpacityEffect()
self.bg_label.setGraphicsEffect(self.opacity_effect)
self.opacity_effect.setOpacity(0.0)
self.fade_timer = QTimer(self)
self.fade_timer.timeout.connect(self.fade_in)
self.fade_timer.start(50)
# スタートボタン
self.start_btn = self.create_button("Start", 350, 250, 100, 50, self.switch_callback, "white")
def fade_in(self):
opacity = self.opacity_effect.opacity()
if opacity < 1.0:
self.opacity_effect.setOpacity(opacity + 0.1)
else:
self.fade_timer.stop()
class TimerSetupScreen(BaseScreen):
def __init__(self, switch_callback):
super().__init__()
self.switch_callback = switch_callback
self.initUI()
def initUI(self):
self.setWindowTitle("EVA Timer - Setup")
self.setGeometry(100, 100, 800, 400)
self.set_background("Eva背景.png")
# 数値入力
self.input_field = QLineEdit(self)
self.input_field.setGeometry(300, 150, 200, 50)
self.input_field.setStyleSheet("font-size: 30px; text-align: center;")
self.input_field.setPlaceholderText("最大999分")
# 設定ボタン
self.set_btn = self.create_button("設定", 350, 250, 100, 50, self.set_timer, "white")
def set_timer(self):
try:
minutes = int(self.input_field.text())
if 1 <= minutes <= 999:
self.switch_callback(minutes)
except ValueError:
pass
class TimerScreen(BaseScreen):
def __init__(self, minutes, switch_callback):
super().__init__()
self.initial_time = minutes * 60 * 1000
self.time_remaining = self.initial_time
self.switch_callback = switch_callback
self.timer = QTimer(self)
self.timer.timeout.connect(self.update_timer)
self.flash_timer = QTimer(self)
self.flash_timer.timeout.connect(self.toggle_flash)
self.is_flashing = False
self.initUI()
def initUI(self):
self.setWindowTitle("EVA Timer")
self.setGeometry(100, 100, 800, 400)
self.set_background("Eva背景.png")
# タイマー表示
font = QFont("DS-Digital", 50)
font.setBold(True)
self.label = QLabel(self.format_time(self.time_remaining), self)
self.label.setFont(font)
self.label.setStyleSheet("color: red; background: transparent;")
self.label.setAlignment(Qt.AlignCenter)
self.label.setGeometry(110, 150, 500, 120)
# ボタン作成
self.start_btn = self.create_button("", 150, 305, 120, 50, self.start_timer)
self.stop_btn = self.create_button("", 256, 305, 113, 50, self.stop_timer)
self.reset_btn = self.create_button("", 362, 305, 108, 50, self.reset_timer)
self.config_btn = self.create_button("", 460, 305, 100, 50, self.switch_callback)
def format_time(self, milliseconds):
total_seconds = milliseconds // 1000
minutes = total_seconds // 60
seconds = total_seconds % 60
hundredths = (milliseconds % 1000) // 10
return f"{minutes:02}:{seconds:02}:{hundredths:02}"
def update_timer(self):
if self.time_remaining > 0:
self.time_remaining -= 60
self.label.setText(self.format_time(self.time_remaining))
else:
self.time_remaining = 0
self.timer.stop()
self.label.setStyleSheet("color: yellow; background: transparent;")
self.flash_timer.start(500)
def toggle_flash(self):
color = "yellow" if self.is_flashing else "black"
self.label.setStyleSheet(f"color: {color}; background: transparent;")
self.is_flashing = not self.is_flashing
def start_timer(self):
if not self.timer.isActive():
self.timer.start(60)
def stop_timer(self):
self.timer.stop()
def reset_timer(self):
self.timer.stop()
self.flash_timer.stop()
self.is_flashing = False
self.label.setStyleSheet("color: red; background: transparent;")
self.time_remaining = self.initial_time
self.label.setText(self.format_time(self.time_remaining))
class AppController:
def __init__(self):
self.app = QApplication(sys.argv)
self.start_screen = StartScreen(self.show_timer_setup)
self.start_screen.show()
def show_timer_setup(self):
self.start_screen.close()
self.timer_setup_screen = TimerSetupScreen(self.show_timer_screen)
self.timer_setup_screen.show()
def show_timer_screen(self, minutes):
self.timer_setup_screen.close()
self.timer_screen = TimerScreen(minutes, self.show_timer_setup)
self.timer_screen.show()
def run(self):
sys.exit(self.app.exec_())
if __name__ == "__main__":
controller = AppController()
controller.run()
🟣 細かい技術ポイント解説(詳細版)
🔹 ① 起動時のフェードイン演出(QGraphicsOpacityEffect + QTimer)
📌 何をしているのか?
- アプリを起動した瞬間、背景画像がふわっと現れるように見せる演出を実装
- これは、UI全体を透明度0 → 1にゆっくり変化させることで実現している
🔧 どうやってる?
pythonコピーする編集するself.opacity_effect = QGraphicsOpacityEffect()
self.bg_label.setGraphicsEffect(self.opacity_effect)
self.opacity_effect.setOpacity(0.0)
QGraphicsOpacityEffectを背景画像に適用し、透明度を制御QTimerを使って50msごとにopacity += 0.1を加算
💡 参考コード
pythonコピーする編集するself.fade_timer = QTimer(self)
self.fade_timer.timeout.connect(self.fade_in)
self.fade_timer.start(50)
pythonコピーする編集するdef fade_in(self):
opacity = self.opacity_effect.opacity()
if opacity < 1.0:
self.opacity_effect.setOpacity(opacity + 0.1)
else:
self.fade_timer.stop()
✅ ポイント
QGraphicsOpacityEffectはPyQtでアニメーション表現を加えるための基本手法- この手法を応用すれば、ボタンやテキストのフェードインも可能
🔹 ② タイマーと点滅処理を分けたQTimerの使い方
📌 なぜタイマーが2つ必要?
self.timer:メインのカウントダウン処理(毎60ms)self.flash_timer:残り0秒で数字を点滅させる処理(500msごとに色を交互に切り替え)
💡 flashの切り替え処理
pythonコピーする編集するdef toggle_flash(self):
color = "yellow" if self.is_flashing else "black"
self.label.setStyleSheet(f"color: {color}; background: transparent;")
self.is_flashing = not self.is_flashing
✅ ポイント
QTimerを機能ごとに分離することで処理がシンプル&保守性UP- ゲームのような状態変化の演出にも応用しやすい書き方
🔹 ③ 背景画像+透明ボタンで“演出重視UI”を実現
📌 目的は?
- UIの印象を“アプリっぽい”から“作品っぽい”ものへ寄せる
🔧 実装方法
pythonコピーする編集するbtn.setStyleSheet(
"background: transparent; border: 2px solid red; color: white; font-size: 20px;"
)
- ボタン背景を透明にし、枠線だけ赤く強調
- 背景画像と調和して、ボタンが“浮いているように”見える
✅ ポイント
- PyQtの
setStyleSheetでCSS風のスタイルを付けられる - 色・サイズ・フォントをうまく使えば、GUIで世界観を演出できる
🔹 ④ QLineEditのテキスト中央揃えの正しい方法
📌 初心者がやりがちなミス
pythonコピーする編集するself.input_field.setStyleSheet("text-align: center;")
→ 実はこれは 無効!
🔧 正しい書き方
pythonコピーする編集するself.input_field.setAlignment(Qt.AlignCenter)
✅ ポイント
text-alignは Web の CSS 用語で、PyQt では無効- PyQt のテキスト中央揃えは
.setAlignment()が正解
🔹 ⑤ フォーマット整形の工夫(ミリ秒 → 分:秒:小数)
pythonコピーする編集するdef format_time(self, milliseconds):
total_seconds = milliseconds // 1000
minutes = total_seconds // 60
seconds = total_seconds % 60
hundredths = (milliseconds % 1000) // 10
return f"{minutes:02}:{seconds:02}:{hundredths:02}"
✅ ポイント
:02の書き方で常に2桁表示(例:5 → 05)- ミリ秒単位でも視認性とデザインを両立したタイマー表記にしている
- 見た目が「精密・近未来風」になり、演出力UP
🟣 まとめと今後やってみたいこと
今回、普段使っていたポモドーロタイマーを「自分の好きなデザインで作ってみたい」という理由から、
PyQtを使ってオリジナルのカウントダウンタイマーを制作しました。
🔧 作ってよかった点
- とにかく楽しかった!
→ 自分好みのUIを自由に組めるのはテンション上がります - GUI開発の流れがわかるようになった
→ 画面遷移、ウィジェットの配置、イベント処理…PyQtの基本が実体験で学べました - Pythonだけで“ちゃんと動くアプリ”が作れたという小さな達成感
🚀 今後の改良アイデア
- タイマーが0になったときに音が鳴る通知機能を追加したい
- 時間プリセット(25分/45分など)をいくつか用意して、ボタン1つで切り替え可能にしたい
- ゆくゆくは**スマホアプリ化(Kivyなど)**にも挑戦してみたい
💬 PyQtやUI制作の魅力について
「見た目にこだわれるプログラミング」って、やっぱり楽しいんだなと改めて感じました。
PyQtはとっつきやすく、GUI開発の取っかかりとしてとても優秀です。
ちょっとずつ手を加えるだけで、アプリが“作品”になっていく過程が面白いです。
✅ 最後に:この記事から得られる読者のメリット
このブログを通して、少しでも以下のようなことを感じてもらえたら嬉しいです。
- 「GUIって面白そう!」「PyQtやってみたい!」と思ってもらえる
- 自分でもUIアプリが作れるんだという可能性を感じてもらえる
- 「お、これ真似して作ってみたいな」と思った方は、コードもあるのでぜひ挑戦してみてください!
このアプリはあくまで“個人の遊び”から始まったものですが、
誰かにとってPyQtやGUI制作のきっかけになれたら、それが一番うれしいです😊

コメント