[PYTHON] I made a simple timer that can be started from the terminal

This article was written as the 9th day article of IS17er Advent Calendar. Click here for the article on Day 8.

Overview

I made a timer app that can be started from the terminal using a library called Tkinter in Python. You can see how it works by looking at the image. Please use it by all means! tmr.png

Yes, let's make a simple timer

Recently, my concentration has dropped, and when I was sitting in front of my computer, two hours had passed before I knew it, so I made a timer that can be easily started from the terminal so that I can concentrate for a short time and manage the time. I decided to do it (yesterday evening).

Concept

(When I was writing this section, I wasn't writing the program yet) As an image of the application to be created this time, for example $ tmr 5m30s -t kadai The image is that if you hit a command like this, a small window will appear in the upper right corner of the screen and count down. -t is optionally the title of the timer. Even if I implement a very complicated function, I will not use it, so I wonder if this is all right for the time being. I wonder if there is an optional notification sound like Slack. After that, if the color changes randomly every time you start it, you can use it without getting bored.

Write

I haven't written a desktop application at all because it's usually a web-based application, but when I googled it a bit, Tkinter seems to be fun, so I wrote this. I decided to use it. It seems that you can easily create a GUI with Python using Tkinter.

code

The code was about 100 lines, so paste it all. Because of this shortness, I made it into one file. Feel free to customize it.

tmr.py


#!/usr/bin/env python
# -*- coding: utf8 -*-
import sys
import re
import argparse
import random
import Tkinter as tk

class App():
  def __init__(self):
    self.window_size = 150
    self.fps = 10
    self.font_size = 23
    self.option = self.parse_args()
    self.time = self.get_timesec()
    self.period = self.time
    self.root = self.set_root()
    self.canvas = self.set_canvas()
    self.label = self.set_label()
    self.progress = self.init_progress()
    self.update()
    self.run()

  def parse_args(self):
    parser = argparse.ArgumentParser(description="Show simple countdown timer on desktop.")
    parser.add_argument("period", action="store", type=str, help="Set period for your timer.")
    parser.add_argument("-t", dest="title", action="store", default="Timer", help="Set title for your timer.")
    parser.add_argument("-n", dest="notify", action="store_false", default=True, help="Disable notification.")
    return parser.parse_args()

  def get_timesec(self):
    if re.search("\A(?:\d+h)?(?:\d+m)?(?:\d+s)?$", self.option.period) is None:
      print "Incorrect format of period:", self.option.period
      print "Set period like 10m30s"
      sys.exit()
    time = 0
    if re.search("\d+h", self.option.period) is not None:
      time += int(re.search("\d+h", self.option.period).group(0)[:-1]) * 3600
    if re.search("\d+m", self.option.period) is not None:
      time += int(re.search("\d+m", self.option.period).group(0)[:-1]) * 60
    if re.search("\d+s", self.option.period) is not None:
      time += int(re.search("\d+s", self.option.period).group(0)[:-1])
    if time > 9 * 3600 + 59 * 60 + 59:
      print "Too long period."
      sys.exit()
    return time

  def set_root(self):
    root = tk.Tk()
    root.resizable(0,0)
    window_size = self.window_size
    colors = ["#f44336", "#E91E63", "#9C27B0", "#673AB7", "#3F51B5", "#2196F3", "#03A9F4", "#00BCD4", "#009688", "#4CAF50", "#8BC34A", "#CDDC39", "#FFEB3B", "#FFC107", "#FF9800", "#FF5722", "#795548", "#9E9E9E", "#607D8B"]
    root.title(self.option.title)
    root.geometry("%dx%d+%d+%d" % (window_size, window_size, root.winfo_screenwidth() - window_size, 0))
    root.configure(bg=random.choice(colors))
    root.attributes("-alpha", 0.5)
    root.attributes("-topmost", True)
    return root

  def set_label(self):
    window_size = self.window_size
    label = self.canvas.create_text((window_size / 2, window_size / 2), text="")
    self.canvas.itemconfig(label, font=("Menlo-Regular", self.font_size))
    return label

  def set_canvas(self):
    window_size = self.window_size
    canvas = tk.Canvas(self.root, width=window_size, height=window_size, highlightthickness=0)
    canvas.grid()
    return canvas

  def format_time(self, timesec):
    m, s = divmod(timesec, 60)
    h, m = divmod(m, 60)
    if h == 0:
      if m == 0:
        return "%02ds" % (s)
      else:
        return "%02dm%02ds" % (m, s)
    else:
      return "%dh%02dm%02ds" % (h, m, s)

  def init_progress(self):
    color = self.root["bg"]
    window_size = self.window_size
    progress = self.canvas.create_arc(window_size * 0.1, window_size * 0.1, window_size * 0.9, window_size * 0.9, style="arc", width="%d" % (window_size / 15), outline=color, start=90, extent=360)
    return progress

  def update(self):
    window_size = self.window_size
    self.canvas.itemconfig(self.label, text=self.format_time(self.time))
    extent = 360.0 * self.time / self.period
    self.canvas.itemconfig(self.progress, start=450-extent, extent=extent)
    self.time -= 1.0 / self.fps
    if self.time < 0:
      if self.option.notify:
        print '\a'
      sys.exit()
    self.root.after(1000 / self.fps, self.update)

  def run(self):
    self.root.mainloop()

app = App()

How to use

I wrote it on GitHub, but here I dare not clone it.

  1. Copy the above code and save it with the file name tmr (no extension).
  2. chmod 755 tmr
  3. Add something like export PATH = $ PATH: / Users / [username] / timer to `~ / .bash_rc``` or `~ / .bash_profile```. If you don't understand this, google the addition of PATH.
  4. Do `source ~ / .bash_rc``` or `source ~ / .bash_profile``` and restart the terminal
  5. Try hitting `` `tmr 10m30s```!

Difficulties (code explanation)

Actually, there are quite a few, but for the time being, there are some.

Regular expressions

It's a regular expression that everyone loves.

"\A(?:\d+h)?(?:\d+m)?(?:\d+s)?$"This is the beginning"\A(?:\d+h)?(?:\d+m)?(?:\d+s)?\z"I wrote that and mass-produced None forever. Get used to regular expressions.



#### How to write code concisely
 Python comes with a large number of useful functions as standard, so it's a good idea to look it up first if you want to write something yourself. In most cases, it's faster to write it yourself, but it's still a bad practice because the corner case processing is troublesome and readability is reduced, so I just googled the existing functions. Moreover, in the case of Python, multiple existing functions often behave in the same way, so it took time to choose a library so that there was a good sense of unity among the existing functions. This may be an occupational disease.

## at the end
 You can clone it from [GitHub](https://github.com/xuzijian629/timer), so please use it (if you have trouble, you can copy the code as it is). I'm glad to cry if you star! Also, notification sounds etc. are not implemented at the time of writing, so I may add them later.

 As an aside, on the Mac there is a genius command called `` `say```
```$ sleep 100 &&say end```
 If you do something like that, it will read "End" after 100 seconds (if the language is set to Japanese).

 Well then, See you tomorrow~

 [Addition] Notification sound has been implemented (the above code has been corrected).
```print '\a'```
 You can go! Isn't it a Python god ...!


Recommended Posts

I made a simple timer that can be started from the terminal
I made a shuffle that can be reset (reverted) with Python
I made a module that can be glitched easily, but I can't pass arguments from entry_points
[Atcoder] [C ++] I made a test automation tool that can be used during the contest
I made a kitchen timer to be displayed on the status bar!
I made a Docker image that can call FBX SDK Python from Node.js
I made a familiar function that can be used in statistics with Python
I made a rigid Pomodoro timer that works with CUI
I investigated the pretreatment that can be done with PyCaret
I made a plug-in that can "Daruma-san fell" with Minecraft
Can I be a data scientist?
I made a tool to automatically generate a simple ER diagram from the CREATE TABLE statement
I made a package that can compare morphological analyzers with Python
From a book that programmers can learn (Python): Find the mode
I made a slack bot that notifies me of the temperature
I made a command to display a colorful calendar in the terminal
I made a program that automatically calculates the zodiac with tkinter
[python] I made a class that can write a file tree quickly
I made a Line bot that guesses the gender and age of a person from an image
I made something with python that NOW LOADING moves from left to right on the terminal
I made a simple blackjack with Python
I created a template for a Python project that can be used universally
I made a calendar that automatically updates the distribution schedule of Vtuber
[Python] I made a utility that can access dict type like a path
I made a library konoha that switches the tokenizer to a nice feeling
ConSinGAN: I tried using GAN that can be generated from one image
I made a module PyNanaco that can charge nanaco credit with python
I started to work at different times, so I made a bot that tells me the time to leave
I made a LINE BOT that returns a terrorist image using the Flickr API
I want to create a priority queue that can be updated in Python (2.7)
I made a simple Bitcoin wallet with pycoin
I made a tool to generate Markdown from the exported Scrapbox JSON file
I couldn't escape from the futon, so I made a fully automatic futon peeling machine.
In Python, I made a LINE Bot that sends pollen information from location information.
I made a simple RSS reader ~ C edition ~
I made a github action that notifies Slack of the visual regression test
I made a twitter app that decodes the characters of Pricone with heroku (failure)
Convert images from FlyCapture SDK to a form that can be used with openCV
[Python] I made a system to introduce "recipes I really want" from the recipe site!
[Django] Hit a command you made from within the process that runs on manage.py.
I tried to make a memo app that can be pomodoro, but a reflection record
The story that sendmail that can be executed in the terminal did not work with cron
[Python / C] I made a device that wirelessly scrolls the screen of a PC remotely.
A mechanism to call a Ruby method from Python that can be done in 200 lines
I made a calendar that automatically updates the distribution schedule of Vtuber (Google Calendar edition)
I made my own Django Middleware so that I can access request information from anywhere
Features that can be extracted from time series data
I made a VM that runs OpenCV for Python
From a book that programmers can learn ... (Python): Pointer
A memo that I touched the Datastore with python
I made a command to markdown the table clipboard
List the classes that can be referenced by ObjCClass
〇✕ I made a game
I made a tool to automatically generate a state transition diagram that can be used for both web development and application development
How to set up a simple SMTP server that can be tested locally in Python
I made a data extension class for tensorflow> = 2.0 because ImageDataGenerator can no longer be used.
[Python] A program to find the number of apples and oranges that can be harvested
Convert mesh data exported from SpriteUV2 to a format that can be imported by Spine
A story that stumbled when I made a chatbot with Transformer
[Python] I started Poetry & Impression that I moved from Pipenv to poetry
I made a simple typing game with tkinter in Python