This article is about Qiita Summer Festival 2020 "If you want to make △△ (app) now using only 〇〇 (language)" It introduces the ** TODO application (distributed) ** created as a theme and explains how to implement it.
I am using ** Python3 (ver 3.7) **, and I have confirmed the operation on Windows 10.
The code is located on the following GitHub. Feel free to use it.
[Code] 1
The application is as follows.
There is a place to display the to-do list at the bottom.
You can select a string from the pull-down to display a specific TODO and it will be displayed by pressing the refresh button.
Select a to-do list to see more details on the list.
It implements three main functions.
This function searches for TODO files saved in multiple specified folders and displays them in a list on the screen.
For example, suppose you store a ** TODO file ** called .txt ** in a folder called ** F: \ Document \ Qiita ** that posts a todo app made only with ** python. In this file, the details of TODO, the deadline for posting the article currently being written, and the deadline for posting, etc. are written.
On the other hand, suppose you have another ** TODO file ** in the folder ** F: \ Document \ python \ django **.
The problem with this situation is that the ** TODO files ** are ** distributed **.
Of course, you can create a ** TODO folder ** and put ** TODO files ** in it, but since you have created and organized the folder hierarchy in your own way, the related files are, for example, ** TODO. I want to put the file ** in the same folder. In addition, important information may be added, such as adding the searched contents to the ** TODO file **.
In order to solve the above problem, ** TODO files ** may be distributed.
This feature only lists ** TODO files ** in a particular folder.
When the number of ** TODO files ** increases, it is troublesome to find the desired ** TODO file ** among all ** TODO files **.
Therefore, we have made it possible to select a folder from the pull-down menu so that only ** TODO files ** stored in a specific folder can be displayed.
This function displays the detailed contents at the bottom of the screen when you click the displayed list of ** TODO files **.
The details are described in the ** TODO file **.
Basically, it may be enough to write a specific ** TODO ** (what to do) with the file name. However, I thought it would be better to see the progress and survey contents, so I implemented it.
Download the code from [GitHub] 1 at the beginning of this article and place it in one folder.
Run ** display.py ** to launch the GUI.
python display.py
The configuration file is a file named ** config.ini ** and is described in the following format.
[Dir_names]
#The name displayed in the pull-down=Absolute path of the folder containing the TODO file
#Example
qiita=F:\Document\800_IT self-learning\09_python\51_Qiita
[File_names]
#The file name you want to display in the TODO list. Wildcard(*)You can use.
#Example
#Files with the string todo at the beginning
todo=todo*
#Files with the extension py
python=*.py
** * Please note that it may take some time to search if there are a large number of folders and files under the listed folder name. ** ** ** It's an extreme story, but if you write C: \ in the folder name, it will take a long time to display the to-do list. ** **
** The character code of the TODO file should be UTF-8. The details screen is not displayed. ** **
operate_file_1.py
import os
import fnmatch
#root:Directory name
#patterns:Supports unix shell-style wildcards. File name pattern
#yeild_folders:Whether to read files under subdirectories.
def all_files(root, patterns = "*", single_level=False, yeild_folders=False):
#split:";"You can specify multiple patterns with.;Cannot be included in the pattern.
patterns = patterns.split(";")
#os.Pass the directory path name, directory name, and file name under the directory specified by walk.
for path, subdirs, files in os.walk(root):
if yeild_folders:
files.extend(subdirs)
files.sort()
for name in files:
for pattern in patterns:
#fnmatch returns True if name matches pattern.
if fnmatch.fnmatch(name, pattern):
yield os.path.join(path, name)
break
if single_level:
break
It will be the code that was written in the Python cookbook.
Python Cookbook 2nd Edition (Page 89, 2.16 Directory Tree Search) by Alex Martelli, Anna Martelli Ravenscroft, David Ascher, Masao Kamozawa, Hitoshi Toyama, Satoshi Yoshida, Sadaki Yoshimune, etc. Translated by O'Reilly Japan ISBN978- 4-87311-276-3
What we are doing is searching for a match from the received directory name and file name and returning the result.
todo.py
from operate_file_1 import all_files
import configparser
class Todo:
def __init__(self):
self.rule_file = configparser.ConfigParser()
self.rule_file.read("./config.ini", "UTF-8")
self.dir_name_keys = list(self.rule_file["Dir_names"].keys())
self.dir_names = [self.rule_file["Dir_names"][key] for key in self.rule_file["Dir_names"].keys()]
self.patterns = [self.rule_file["File_names"][key] for key in self.rule_file["File_names"].keys()]
def search_file(self):
paths = {}
for index, dir_name in enumerate(self.dir_names):
paths[self.dir_name_keys[index]] = list(all_files(dir_name, ";".join(self.patterns)))
return paths
def limit_search_file(self, dir_name_key):
paths = {}
paths[dir_name_key] = list(all_files(self.rule_file["Dir_names"][dir_name_key], ";".join(self.patterns)))
return paths
def get_dir_name_keys(self):
return self.dir_name_keys
This is a class that reads the settings from ** config.ini ** and searches the file using the function described in ** operator_file_1.py **.
Use the module built into python called ** [configparser] 2 ** to read the value from the config file.
** search \ _file ** searches the entire search, ** limit \ _search \ _file ** searches only the specified folder.
display.py
import tkinter as tk
import os
import datetime
from todo import Todo
from gui_object import Frame, Label, Listbox, Text, Button, Combobox
class TodoDisplay:
def __init__(self):
self.root = tk.Tk()
self.root.title("todo")
self.todo = Todo()
self.todo_list_box_dict = {}
self.todo_list_frame = Frame(self.root)
self.todo_list_frame.grid(column=0, row=1)
self.todo_detail_frame = Frame(self.root)
self.todo_detail_frame.grid(column=0, row=2)
self.function_frame = Frame(self.root)
self.function_frame.grid(column=0, row=0)
self.listbox = Listbox(master=self.todo_list_frame, master_of_detail_text=self.todo_detail_frame)
self.refresh_button = Button(master=self.function_frame)
self.refresh_button.grid(column=1, row=0)
self.refresh_button["text"] = "update"
self.refresh_button["command"] = self.refresh
self.combbox = Combobox(master=self.function_frame)
self.combbox.grid(column=0, row=0)
self.set_value_for_combbox()
def display_todo(self):
todo_list_box_id = 0
self.todo_list_box_dict = {}
if (self.combbox.get() == "all") or (self.combbox.get() == ""):
paths = self.todo.search_file()
else:
paths = self.todo.limit_search_file(self.combbox.get())
for key in paths.keys():
for path in paths[key]:
create_time, update_time = self.get_timestamp_of_path(path)
insert_statement = " ".join(["Create", create_time, "update", update_time, path.split("\\")[-1].split(".")[0]])
self.listbox.insert(todo_list_box_id, insert_statement)
self.todo_list_box_dict[todo_list_box_id] = path
todo_list_box_id = todo_list_box_id + 1
self.listbox.set_todo_list(self.todo_list_box_dict)
def get_timestamp_of_path(self, path):
stat_result = os.stat(path)
create_time = datetime.datetime.fromtimestamp(stat_result.st_ctime).strftime("%Y/%m/%d %H:%M:%S")
update_time = datetime.datetime.fromtimestamp(stat_result.st_mtime).strftime("%Y/%m/%d %H:%M:%S")
return create_time, update_time
def refresh(self, event=None):
self.listbox.delete(0, "end")
self.display_todo()
def set_value_for_combbox(self):
self.combbox["value"] = ["all"] + [dir_name.split("\\")[-1] for dir_name in self.todo.get_dir_name_keys()]
def mainloop(self):
self.root.mainloop()
if __name__ == "__main__":
todo_display = TodoDisplay()
todo_display.display_todo()
todo_display.mainloop()
I am using the ** Tkinter ** object to display a list of ** TODO files ** searched in ** todo.py **.
gui_object.py
from tkinter import *
import tkinter as tk
import tkinter.ttk as ttk
class Frame(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.grid(column=0, row=0)
self["width"] = 100
self["height"] = 100
self["padx"] = 20
self["pady"] = 20
class Button(tk.Button):
def __init__(self, master=None):
tk.Button.__init__(self, master)
self["height"] = 2
self["width"] = 20
self["font"] = ("Helvetica", 15)
class RefreshButton(Button):
def __init__(self, master=None,):
Button.__init__(self, master)
class Combobox(ttk.Combobox):
def __init__(self, master=None):
ttk.Combobox.__init__(self, master)
self["font"] = ("Helvetica", 20)
class Text(tk.Text):
def __init__(self, master=None):
tk.Text.__init__(self, master)
self["width"] = 100
self["height"] = 10
class Listbox(tk.Listbox):
def __init__(self, master=None, master_of_detail_text=None):
scrollbar = Scrollbar(master)
scrollbar.pack(side=RIGHT, fill=Y)
tk.Listbox.__init__(self, master, yscrollcommand=scrollbar.set, selectmode=EXTENDED)
self.pack(side=LEFT, fill=BOTH)
self["width"] = 100
self["height"] = 10
self["font"] = ("Helvetica", 12)
self.master = master
self.master_of_detail_text = master_of_detail_text
self.text = Text(self.master_of_detail_text)
scrollbar["command"] = self.yview
self.bind("<Double-Button-1>", self.show_detail)
self.bind("<Return>", self.show_detail)
self.todo_list = {}
def show_detail(self, event=None):
self.text.destroy()
self.text = Text(self.master_of_detail_text)
self.text.insert(END, self.read_detail_of_todo(self.index(ACTIVE)))
self.text.pack()
def set_todo_list(self, todo_list_dict):
self.todo_list = todo_list_dict
def read_detail_of_todo(self, index):
path = self.todo_list[index]
with open(path, encoding="utf_8") as f:
return f.read()
Settings are made for various ** Tkinter ** objects used in ** display.py **.
I mainly adjust the appearance.
It was a fun and happy time to create an application with a fixed theme.
I hope I can use this TODO app conveniently from now on.
Recommended Posts