If you want to make a TODO application (distributed) now using only Python

Summary of this article

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

What is a TODO application (distributed)?

Appearance

The application is as follows.

todo.png

todo.gif

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.

function

It implements three main functions.

List TODO files stored in multiple folders

function_一覧表示.png

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.

List only TODOs stored in a specific folder

function_特定のフォルダを一覧表示.png

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.

Display the detailed contents of the TODO file

function_詳細表示.png

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.

How to use

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. ** **

About implementation

File search

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 search

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.

todo display

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 parts

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.

At the end

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

If you want to make a TODO application (distributed) now using only Python
If you were to create a TODO application (distributed) using only Python-extension 1
If you want to make a Windows application (exe) that can be actually used now using only Python
I tried to make a todo application using bottle with python
I want to make a web application using React and Python flask
If you want to make a discord bot with python, let's use a framework
If you know Python, you can make a web application with Django
If you want to assign csv export to a variable in python
[Python] If you want to draw a scatter plot of multiple clusters
I want to make a game with Python
If you want to create a Word Cloud.
If you want to display values using choices in a template in a Django model
(Python) Try to develop a web application using Django
How to make a Python package using VS Code
[Python] I want to make a nested list a tuple
[Django] A memorandum when you want to communicate asynchronously [Python3]
How to transpose a 2D array using only python [Note]
I tried to make a stopwatch using tkinter in python
I want to make input () a nice complement in python
When you want to hit a UNIX command on Python
I want to make a voice changer using Python and SPTK with reference to a famous site
If you want to become a data scientist, start with Kaggle
Don't write Python if you want to speed it up with Python
What to do if you get a minus zero in Python
I tried to make a regular expression of "amount" using Python
I tried to make a regular expression of "time" using Python
I want to know if you install Python on Mac ・ Iroha
I tried to make a regular expression of "date" using Python
I tried to make a 2channel post notification application with Python
[Introduction] I want to make a Mastodon Bot with Python! 【Beginners】
Check if you can connect to a TCP port in Python
When you want to replace multiple characters in a string without using regular expressions in python3 series
If you want a singleton in python, think of the module as a singleton
[Introduction to Udemy Python3 + Application] 33. if statement
What to do if you get a "Wrong Python Platform" warning when using Python with the NetBeans IDE
[Python] How to make a class iterable
If you want to include awsebcli with CircleCI, specify the python version
I want to build a Python environment
[Streamlit] I hate JavaScript, so I make a web application only with Python
If you want to count words in Python, it's convenient to use Counter.
[Python] What to do if you get a ModuleNotFoundError when importing pandas using Jupyter Notebook in Anaconda
I want to do a monkey patch only partially safely in Python
I tried to make a simple mail sending application with tkinter of Python
Use PIL in Python to extract only the data you want from Exif
[Python] Smasher tried to make the video loading process a function using a generator
What to do if you get "Python not configured." Using PyDev in Eclipse
I want to make matplotlib a dark theme
A memorandum to make WebDAV only with nginx
Would you like to make a Twitter resume?
I want to create a window in Python
I want to email from Gmail using Python.
Try to make a "cryptanalysis" cipher with Python
Steps to develop a web application in Python
Try to make a dihedral group with Python
I want to make C ++ code from Python code!
Let's make a module for Python using SWIG
I want to write to a file with Python
I tried to make a ○ ✕ game using TensorFlow
Lark Basics (Python, Lark to make a shell-like guy)
A convenient function memo to use when you want to enter the debugger if an error occurs when running a Python script.
Ansible self-made module creation-Part 4: Life you want to make without a named pipe (complete)-