はじめに
本記事では、PythonとTkinterを使用して、完全自由移動可能なカンバン方式のタスク管理アプリケーションを作成する方法を解説します。このアプリケーションは、シンプルながら柔軟性の高いタスク管理ツールとして機能し、プログラミング初心者からある程度経験のある方まで、幅広い読者に役立つ内容となっています。
要件
アプリケーションの主な要件は以下の通りです:
- カンバン方式のタスク管理(ToDo, Doing, Done の3列)
- タスクの追加、編集、削除機能
- タスク間の自由な移動(任意の列間で双方向に移動可能)
- タスクの色変更機能
- シンプルで直感的なユーザーインターフェース
仕様
アプリケーションの詳細仕様は以下の通りです:
-
ユーザーインターフェース
- ウィンドウサイズ: 800x600ピクセル
- 3つの列(ToDo, Doing, Done)を持つカンバンボード
- 各列の背景色を区別(ToDo: 薄い赤、Doing: 薄い緑、Done: 薄い青)
- 新規タスク入力フィールドと追加ボタン
-
タスク管理
- 新規タスクは常に「ToDo」列に追加
- 各タスクカードに以下の機能を実装:
- 左右の列への移動ボタン
- 編集ボタン
- 色変更ボタン
- 削除ボタン
-
タスクの移動
- 左右の矢印ボタンで隣接する列へ移動可能
- ToDo ⇔ Doing ⇔ Done の任意の方向に移動可能
-
タスクの編集
- タスクの説明文を編集可能
- タスクカードの色をカラーピッカーで自由に変更可能
-
データ管理
- アプリケーション実行中はメモリ上でタスクを管理
- (オプション)タスクデータの永続化は今後の拡張機能として検討
クラス図
シーケンス図
実装
それでは、上記の要件と仕様に基づいて、アプリケーションの実装を行っていきます。
1. 必要なモジュールのインポート
まず、必要なモジュールをインポートします。
import tkinter as tk
from tkinter import ttk, messagebox, colorchooser, simpledialog
import random
2. タスククラスの定義
個々のタスクを表現するクラスを定義します。
class Task:
def __init__(self, description, status="ToDo", color=None):
self.description = description
self.status = status
self.color = color or self.random_color()
def random_color(self):
return "#{:06x}".format(random.randint(0x808080, 0xFFFFFF))
3. タスクカードクラスの定義
タスクカードのUIと機能を実装するクラスを定義します。
class TaskCard(tk.Frame):
def __init__(self, master, task, delete_callback, move_callback, edit_callback, **kwargs):
super().__init__(master, bg=task.color, bd=1, relief=tk.RAISED, **kwargs)
self.task = task
self.delete_callback = delete_callback
self.move_callback = move_callback
self.edit_callback = edit_callback
self.description = tk.Label(self, text=task.description, bg=task.color, wraplength=150)
self.description.pack(expand=True, fill=tk.BOTH, padx=5, pady=5)
button_frame = tk.Frame(self, bg=task.color)
button_frame.pack(fill=tk.X, padx=2, pady=2)
self.left_button = ttk.Button(button_frame, text="←", width=2, command=self.move_left)
self.left_button.pack(side=tk.LEFT, padx=(0, 2))
self.right_button = ttk.Button(button_frame, text="→", width=2, command=self.move_right)
self.right_button.pack(side=tk.LEFT, padx=(0, 2))
edit_button = ttk.Button(button_frame, text="編集", width=4, command=self.edit_task)
edit_button.pack(side=tk.LEFT, padx=(0, 2))
color_button = ttk.Button(button_frame, text="色", width=3, command=self.change_color)
color_button.pack(side=tk.LEFT, padx=(0, 2))
delete = ttk.Button(button_frame, text="×", width=2, command=self.delete)
delete.pack(side=tk.RIGHT)
self.update_move_buttons()
def move_left(self):
self.move_callback(self.task, self.task.status, -1)
def move_right(self):
self.move_callback(self.task, self.task.status, 1)
def delete(self):
self.delete_callback(self.task, self.task.status)
def edit_task(self):
new_description = simpledialog.askstring("タスクの編集", "新しい説明を入力してください:", initialvalue=self.task.description)
if new_description:
self.edit_callback(self.task, new_description)
self.description.config(text=new_description)
def change_color(self):
new_color = colorchooser.askcolor(title="タスクの色を選択")[1]
if new_color:
self.task.color = new_color
self.config(bg=new_color)
self.description.config(bg=new_color)
for widget in self.winfo_children():
if isinstance(widget, tk.Frame):
widget.config(bg=new_color)
def update_move_buttons(self):
order = ["ToDo", "Doing", "Done"]
current_index = order.index(self.task.status)
self.left_button.config(state="normal" if current_index > 0 else "disabled")
self.right_button.config(state="normal" if current_index < len(order) - 1 else "disabled")
4. カンバンボードクラスの定義
アプリケーションのメインウィンドウとカンバンボードの機能を実装するクラスを定義します。
class KanbanBoard(tk.Tk):
def __init__(self):
super().__init__()
self.title("タスク管理アプリ")
self.geometry("800x600")
self.configure(bg="#f0f0f0")
self.columns = ["ToDo", "Doing", "Done"]
self.tasks = {column: [] for column in self.columns}
self.create_widgets()
def create_widgets(self):
self.create_header()
self.create_board()
self.create_task_entry()
def create_header(self):
header = tk.Frame(self, bg="#4a4a4a")
header.pack(fill=tk.X, padx=10, pady=10)
title = tk.Label(header, text="タスク管理アプリ", font=("Helvetica", 18, "bold"), bg="#4a4a4a", fg="white")
title.pack(side=tk.LEFT, padx=10)
def create_board(self):
board = tk.Frame(self)
board.pack(expand=True, fill=tk.BOTH, padx=10, pady=10)
colors = {"ToDo": "#ffcccc", "Doing": "#ccffcc", "Done": "#ccccff"}
for i, column in enumerate(self.columns):
column_frame = tk.Frame(board, bg=colors[column], bd=2, relief=tk.RAISED)
column_frame.grid(row=0, column=i, padx=5, pady=5, sticky="nsew")
column_label = tk.Label(column_frame, text=column, font=("Helvetica", 14, "bold"), bg=colors[column])
column_label.pack(pady=10)
task_frame = tk.Frame(column_frame, bg=colors[column])
task_frame.pack(expand=True, fill=tk.BOTH, padx=5, pady=5)
self.tasks[column] = []
board.columnconfigure(i, weight=1)
board.rowconfigure(0, weight=1)
def create_task_entry(self):
entry_frame = tk.Frame(self, bg="#f0f0f0")
entry_frame.pack(fill=tk.X, padx=10, pady=10)
self.task_entry = ttk.Entry(entry_frame, font=("Helvetica", 12), width=40)
self.task_entry.pack(side=tk.LEFT, padx=(0, 10))
add_button = ttk.Button(entry_frame, text="タスクを追加", command=self.add_task)
add_button.pack(side=tk.LEFT)
def add_task(self):
description = self.task_entry.get().strip()
if description:
task = Task(description)
self.create_task_card("ToDo", task)
self.task_entry.delete(0, tk.END)
else:
messagebox.showwarning("警告", "タスクの説明を入力してください。")
def create_task_card(self, column, task):
column_frame = self.winfo_children()[1].grid_slaves(row=0, column=self.columns.index(column))[0]
task_frame = column_frame.winfo_children()[1]
card = TaskCard(task_frame, task, delete_callback=self.delete_task,
move_callback=self.move_task, edit_callback=self.edit_task)
card.pack(fill=tk.X, padx=5, pady=5)
task.status = column
self.tasks[column].append((task, card))
def move_task(self, task, from_column, direction):
current_index = self.columns.index(from_column)
new_index = current_index + direction
if 0 <= new_index < len(self.columns):
to_column = self.columns[new_index]
self.delete_task(task, from_column)
self.create_task_card(to_column, task)
task.status = to_column # タスクのステータスを更新
self.update_all_task_cards() # すべてのタスクカードを更新
def delete_task(self, task, column):
for t, card in self.tasks[column]:
if t == task:
card.destroy()
self.tasks[column].remove((t, card))
break
def edit_task(self, task, new_description):
task.description = new_description
# タスクカードの表示を更新
for column in self.tasks:
for t, card in self.tasks[column]:
if t == task:
card.description.config(text=new_description)
break
def update_all_task_cards(self):
for column in self.tasks:
for task, card in self.tasks[column]:
card.update_move_buttons()
if __name__ == "__main__":
app = KanbanBoard()
app.mainloop()
実行方法
上記のコードを kanban_task_manager.py
として保存し、以下のコマンドで実行します:
python kanban_task_manager.py
機能説明
- タスクの追加: 下部の入力フィールドにタスクの説明を入力し、「タスクを追加」ボタンをクリックします。
- タスクの移動: タスクカード上の「←」「→」ボタンで左右の列に移動できます。
- タスクの編集: 「編集」ボタンをクリックし、新しい説明を入力します。
- タスクの色変更: 「色」ボタンをクリックし、カラーピッカーから新しい色を選択します。
- タスクの削除: 「×」ボタンをクリックしてタスクを削除します。
まとめ
このアプリケーションでは、PythonとTkinterを使用して、カンバン方式のタスク管理ツールを実装しました。完全自由移動可能なタスク、カラフルなタスクカード、シンプルで直感的なUIにより、効率的なタスク管理が可能です。
発展的な機能追加のアイデア
- データの永続化(ファイルやデータベースへの保存)
- タスクの優先度設定
- 締め切り日の追加
- 複数のプロジェクトやボードの管理
- ドラッグ&ドロップによるタスク移動
これらの機能を追加することで、より高度なタスク管理アプリケーションを作成できます。
ぜひ、このコードを基に自分なりのカスタマイズを加えて、理想のタスク管理ツールを作ってみてください!