PythonでTkinterを使ったTodoリストを作りました

作ったもの



新年の目標をたてたこともあり、自分の1日のタスク消化用に簡単なTodoリストをPythonで作りたいと思ったところ、
Tkinterというライブラリを使えば簡単にできるようでしたので、
このTkinterを使ってTodoリストを作成してみました。





f:id:mtomitomi:20180103211702p:plain





テキストボックスに入力して「ADD」ボタンを押すと、タスクとして表示されます。



f:id:mtomitomi:20180103211500p:plain





タスクの右にある「DONE」ボタンを押すとタスクが削除され、
残りのタスクが上に詰めて表示されます。



f:id:mtomitomi:20180103211518p:plain






Tkinterとは



Tkinter(ティーキンター、ティーケーインター)はGUIを開発するためのライブラリです。
Windows、Mac OSX、LinuxといったOSに対応しています。(クロスプラットフォームと言います)



1988年にTcl(ティクル)というシンプルな仕組みのスクリプト言語が登場し、
その後にTclでGUIを開発するためのツールキットとしてTk(Tool Kitの略)が開発されました。
これらを「Tcl/Tk」と呼び、TkinterはこのTcl/TkをPythonで扱うためのライブラリとして登場しました。





GUIとは

Graphical User Interface(グラフィカル・ユーザ・インタフェース)の略で、
コンピュータへ出す命令や指示等を、ユーザが画面上で視覚的に捉えて行動を指定できるものです。






他のGUIライブラリ



Tkinterは古いライブラリなので、流行りを使いたい人は、kivyなどを使ったほうが良いと思います。(学習難易度は高そうです)

下記にGUIライブラリの比較が載っています。



PythonのGUIライブラリを比較 | ハジプロ!






Tkinterのメリット



Python3に標準で付いているので、インストールする手間がかかりません。

また、言語仕様がシンプルなので誰でも簡単にGUIを作成することができます。

昔からあるせいか、世界のGoogleトレンドで比較するとTkinterが1番使われているようです。






Tkinterのデメリット



巷では、見た目が古臭くてダサいと言われているようです。
ただこの見た目は、Python3系の標準に入っているTk8.5で大きく改善されました。

機能面が少し乏しく、複雑な機能の実装は難しいようです。






参考にした資料



・公式ドキュメント



25.1. tkinter — Tcl/Tk の Python インタフェース — Python 3.6.3 ドキュメント





www.geocities.jp



qiita.com



qiita.com






実装



何番目の「DONE」ボタンが押されたのか判定するための値が、ボタン名でしか取得できそうになかったので、ボタン名に番号を付けています。

もっと良い方法があるのかもしれませんが、他の方法がわからなかったので苦肉の策でこの方法で実装しています。

また、MacOSだと挙動がおかしい部分がいくつかあるようでした。
ボタンの色はbg='green'で指定できるはずなのですが、
MacOSだと上手く動作しないようなので、highlightbackground='green'としています。

また、テキストボックスに日本語で入力しようとすると、確定前の文字が表示されません。
これもMacOSで発生する事象のようです。





完成したTodoリストのコードです。




import tkinter
from tkinter import messagebox

class Application(tkinter.Frame):
def __init__(self, master=None):
tkinter.Frame.__init__(self, master)
self.master.title('Todo List')
self.master.geometry('600x600')

self.num = 1 # タスクの行番号
self.todo = "" # 追加するタスク
self.button = "" # 追加するタスクの削除ボタン

self.taskValueList = [] # 画面に表示するタスクの値
self.todoList = [] # 画面に表示する追加タスク
self.buttonList = [] # 画面に表示する追加タスクの削除ボタン

self.pack()
self.create_widgets()

def create_widgets(self):
# 初期表示の部品を作成
self.label = tkinter.Label(self, text="Todo List", font=("", 25))
self.editBox = tkinter.Entry(self, width=30)
self.whiteLabel = tkinter.Label(self, text="")

# 初期表示の部品を配置
self.label.grid(row=0, column=0)
self.editBox.grid(row=1, column=0)
self.whiteLabel.grid(row=2, column=0)

# 追加ボタンを生成
self.button = tkinter.Button(self, width=10, text="ADD", highlightbackground='green')
self.button.bind("<Button-1>", self.add_value)
self.button.grid(row=1, column=1)

# 追加ボタン押下時の処理
def add_value(self, event):
# テキストボックスの値を取得
task = tkinter.StringVar()
task.set(self.editBox.get())
self.taskValueList.append(task)

# 新規タスクを追加
self.todo = tkinter.Label(self, width=30, textvariable=task)
self.todo.grid(row=3+self.num, column=0, sticky=tkinter.W)
self.todoList.append(self.todo)

# 新規タスクの削除ボタンを追加
self.button = tkinter.Button(self, width=10, text="DONE "+ str(self.num), highlightbackground='gray')
self.button.bind("<Button-1>", self.delete_value)
self.button.grid(row=3+self.num, column=1)
self.buttonList.append(self.button)

self.num += 1

# テキストボックスを初期化
self.editBox.delete(0, tkinter.END)

# 削除ボタン押下時の処理
def delete_value(self, event):
# 削除するタスク番号を取得
deletenum = int(event.widget["text"].split(" ")[1])
index = deletenum-1

# 削除前のタスクを一時保存
tmpTaskValueList = []
tmpTodoList = []
tmpButtonList = []
tmpTaskValueList = self.taskValueList
tmpTodoList = self.todoList
tmpButtonList = self.buttonList

# 削除後のタスクを格納するオブジェクト
newTaskValueList = []
newTodoList = []
newButtonList = []

for i in range(len(self.todoList)):

# 削除する番号より前のタスクはそのままにする
if i < index:
# 削除後タスクListに追加する
newTaskValueList.append(self.taskValueList[i])
newTodoList.append(self.todoList[i])
newButtonList.append(self.buttonList[i])
continue

# 削除する番号のタスクを削除
elif i == index:
self.todoList[i].destroy()
self.buttonList[i].destroy()

# 削除する番号より後ろのタスクは1つずつ前へ詰める
else:
self.todoList[i].destroy()
self.buttonList[i].destroy()

# 1つ前へ詰めたタスクの値を新規作成
self.todo = tkinter.Label(self, width=30, textvariable=tmpTaskValueList[i])
self.todo.grid(row=4+i, column=0, sticky=tkinter.W)

# 1つ前へ詰めた削除ボタンを新規作成
self.button = tkinter.Button(self, width=10, text="DONE "+ str(i), highlightbackground='gray')
self.button.bind("<Button-1>", self.delete_value)

# 削除後タスクListに追加する
newTaskValueList.append(tmpTaskValueList[i])
newTodoList.append(self.todo)
newButtonList.append(self.button)

self.button.grid(row=4+i, column=1)

# 削除後タスクListを元々のタスクListに置き換える
self.taskValueList = []
self.todoList = []
self.buttonList = []
self.taskValueList = newTaskValueList
self.todoList = newTodoList
self.buttonList = newButtonList
self.num -= 1

root = tkinter.Tk()
root.title("todo")
app = Application(master=root)
app.mainloop()






Tkinterを使ってみた感想



エディタさえあれば誰でも気軽に使えて仕様も簡単なので、アルゴリズムの勉強をしたり、
すぐに簡単なアプリを作りたい場合には最適かと思います。

挙動がおかしい部分があるので業務への導入は難しそうですが、
この作ったTodoアプリを自分のタスク消化に活用しようと思います。

PythonGUIアプリを作れることを知らなかったので勉強になりました。





次回アプリを作成するときは、実際にもよく使われているPythonフレームワークDjango」を試してみたいと思っています。




2 Comments

  1. インデントが消滅しています!!!!!!!

    返信削除
  2. 最近メンテナンスをしておらず、気づいておりませんでしたが修正しておきます。
    ご連絡ありがとうございます。

    返信削除