本記事の概要
本記事はQiita夏祭り2020の 「 〇〇(言語)のみを使って、今△△(アプリ)を作るとしたら」 を
テーマにして作成したTODOアプリケーション(分散型)の機能拡張について説明を行うものです。
Pythonのみを使って、今TODOアプリケーション(分散型)を作るとしたらで執筆したTODOアプリケーションを
機能拡張してみました。
Qiita夏祭りのためだけに作ったつもりが楽しくなってきたので、これからゆっくりと拡張していこうと思います。
使用しているのは**Python3(ver 3.7)**であり、Windows10上で動作確認をしております。
コードは以下のGitHubに配置しております。ご自由にお使いください。
拡張後のTODOアプリケーション(分散型)とは
見た目
以下のようなアプリケーションになりました。
拡張された機能
拡張した機能は以下の4つになります。
- TODOファイルに記載されたメタデータの表示
- 期限日、重要度を基にしたTODOファイルのソート
- TODOファイルの名前につけた重要度による色分け
- TODO詳細画面からパスをクリックすることでテキストエディタを開く
TODOファイルに記載されたメタデータの表示
メタデータはTODO名の後に期限やカテゴリとして追加されています。
メタデータを表示したい場合はTODOファイルの先頭行に**#メタデータ1 メタデータ2**と記載していきます。
#2020/7/12 修正
todoの詳細
メタデータ自体はconfig.iniによって管理しています。
[Meta_data]部分です。数字(連番)の後にメタデータとして表示したいキー名を入力します。
この設定ファイルだと**#2020/07/29とTODOファイル**の先頭行に記載すれば、TODO一覧の該当TODOへ
期限:2020/07/29という文字列が追記されます。
1番のメタデータは要らないけど、2番のメタデータは欲しい、つまりカテゴリだけ表示したい場合は、
#[半角スペース1文字]カテゴリ名とします。半角スペース文字を区切り文字としているので、
不要なメタデータは半角スペース1文字を入れましょう。
[Dir_names]
django=F:\Document\800_IT自己学習\09_python\10_Django
kivy=F:\Document\800_IT自己学習\09_python\14_Kivy
qiita=F:\Document\800_IT自己学習\09_python\51_Qiita
[File_names]
todo=*todo*.txt
[Meta_data]
1=期限
2=カテゴリ
期限日、重要度を基にしたTODOファイルのソート
左側のプルダウンからソート方法を選択し、更新ボタンを押すことでソートできます。
importanceは重要度、limitは期限です。
importanceは設定ファイルのImportance_colorで使用されている重要度を使用します。
重要度は基本的にはA、B、Cのようなローマ字か数字にすると分かりやすいかと思います。
ここは設定ファイルを変更することで好きなように出来ます。
[Dir_names]
django=F:\Document\800_IT自己学習\09_python\10_Django
kivy=F:\Document\800_IT自己学習\09_python\14_Kivy
qiita=F:\Document\800_IT自己学習\09_python\51_Qiita
[File_names]
todo=*todo*.txt
[Importance_color]
default=white
A=red
B=yellow
C=green
[Meta_data]
1=期限
2=カテゴリ
TODOファイルの名前につけた重要度による色分け
これは設定ファイルに記載した重要度と色の対応をもとに、TODOリストの色を変える機能です。
上記で説明しているようにconfig.iniの**[Importance_color]**を設定し、好きな色にすることが出来ます。
TODO詳細画面からパスをクリックすることでテキストエディタを開く
TODO詳細画面にファイルパスを記載しており、そこをダブルクリックすることでOS既定のプログラムを使用してTODOファイルを開くことが出来ます。
私の環境では**.txt**はサクラエディタで開くことになっているため、TODO詳細画面からファイルパスをクリックするとサクラエディタが開きます。
使い方
詳しくは前回の投稿ページに記載しております。
今回更新した設定ファイルはImportance_colorとMeta_dataです。
[Dir_names]
#プルダウンに表示される名前=TODOファイルを格納しているフォルダの絶対パス
#例
qiita=F:\Document\800_IT自己学習\09_python\51_Qiita
[File_names]
#TODOリスト一覧に表示したいファイル名。ワイルドカード(*)を使用できます。
#例
#先頭にtodoという文字列があるファイル
todo=todo*
#拡張子がpyのファイル
python=*.py
[Importance_color]
#重要度を表すためにファイル名で使用する文字列と対応する色
#defaultは重要度の文字列が入っていないファイルに対して使用される色です。
#色はredやblueなどの文字列で指定します。
default=white
A=red
B=yellow
C=green
[Meta_data]
#メタデータとして使用するファイル内の文字列の位置とメタデータのキー名
#TODOファイルの先頭行のうち#から始まるものをメタデータとして認識します。
# example: #2020/09/01 機能追加 とすれば期限:2020/09/01 カテゴリ:機能追加となります。
# # 機能追加 とすればカテゴリ:機能追加となります(半角スペース1文字があるため期限は無視される)。
1=期限
2=カテゴリ
実装
TODOファイルに記載されたメタデータの表示
これは単純にTodoクラスにsearch_mata_dataメソッドを実装し、メタデータのリストを返してもらうようにしました。
メタデータのリストをlistbox.insertで表示しています。
...
class TodoDisplay:
def display_todo(self):
...
for path in paths:
metadata_list = self.todo.search_meta_data(path)
insert_statement_list = [path.split("\\")[-1].split(".")[0]]
insert_statement_list.extend(metadata_list)
insert_statement = " ".join(insert_statement_list)
self.listbox.insert(todo_list_box_id, insert_statement)
...
...
class Todo:
...
def search_meta_data(self, path):
linecache.clearcache()
first_line = linecache.getline(path, 1)
if "#" == first_line[0]:
metadata_list = first_line[1:].split(" ")[:len(self.rule_file["Meta_data"].keys())]
display_metadata_list = []
for i, metadata in enumerate(metadata_list):
if metadata != "":
display_metadata_list.append(":".join([self.rule_file["Meta_data"][str(i+1)], metadata]))
return display_metadata_list
else:
return [""]
...
期限日、重要度を基にしたTODOファイルのソート
TodoDisplayクラスからTodoクラスにあるsort_todoメソッドを呼び出します。
引数として**combbox(プルダウン)**で選択されている文字列を渡し、sort_todoで渡された文字列に応じて呼び出す関数を変えています。
class TodoDisplay:
...
def display_todo(self):
...
if self.sort_combbox.get() == "":
pass
else:
paths = self.todo.sort_todo(paths, method=self.sort_combbox.get())
...
...
class Todo:
...
def sort_todo(self, paths, method):
if method == "importance":
return self.sort_importance(paths)
elif method == "limit":
return self.sort_todo_limit(paths)
def sort_importance(self, paths):
path_dicts = []
for path in paths:
path_dict = {}
file_name = path.split("\\")[-1].split(".")[0]
result = self.judge_importance(file_name)
if result is None:
path_dict["importance"] = "z"
else:
path_dict["importance"] = result.group()[1]
path_dict["path"] = path
path_dicts.append(path_dict)
sorted_path_dicts = sorted(path_dicts, key=lambda x: x["importance"])
sorted_paths = [sorted_path_dict["path"] for sorted_path_dict in sorted_path_dicts]
return sorted_paths
def sort_todo_limit(self, paths):
path_dicts = []
for path in paths:
path_dict = {}
first_metadata = self.search_meta_data(path)[0]
if self.rule_file["Meta_data"]["1"] in first_metadata:
path_dict["metadata_todo_limit"] = first_metadata.split(":")[-1]
else:
path_dict["metadata_todo_limit"] = "9999/12/31"
path_dict["path"] = path
path_dicts.append(path_dict)
sorted_path_dicts = sorted(path_dicts, key=lambda x: x["metadata_todo_limit"])
sorted_paths = [sorted_path_dict["path"] for sorted_path_dict in sorted_path_dicts]
return sorted_paths
...
TODOファイルの名前につけた重要度による色分け
Todoクラスのsearch_importanceメソッドへファイルパスを渡し、対応する色を返してもらいます。
その後、listbox.itemconfigを使用して色付けを行います。
リストボックスの個々の行へ色付けする方法は以下のリンクを参考にしました。
Is it possible to colour a specific item in a Listbox widget?
class TodoDisplay:
...
def display_todo(self):
...
for path in paths:
...
importance_color = self.todo.search_importance(path.split("\\")[-1].split(".")[0])
self.listbox.itemconfig(todo_list_box_id, {'bg': importance_color})
...
...
...
TODO詳細画面からパスをクリックすることでテキストエディタを開く
text.tag_bindを使用することで、イベントをテキストの文字列に紐づけることができます。
またos.system("start " + path)とすることで、OSに設定されている既定のプログラムでpathのファイルを開けます。
class Listbox(tk.Listbox):
...
def show_detail(self, event=None):
...
self.text = Text(self.master_of_detail_text)
self.text.tag_config('system_message', background="white", foreground="blue", underline=1)
self.text.tag_bind("system_message", "<Double-Button-1>", self.open_with_another_app)
self.text.insert(END, self.get_todo_list()[self.index(ACTIVE)], "system_message")
...
...
def open_with_another_app(self, event=None):
path = self.get_todo_list()[self.index(ACTIVE)]
os.system("start " + path)
終わりに
なんだか、アプリケーションを成長させるのが楽しくなってきました。
RPGでキャラクターを育てている感覚と同じかもしれません。
これから使っていって、もっと拡張していけたらと思います。そろそろUIもしっかりさせようと。