0

I'm trying to learn how to use TKinter to make GUI for Python apps. I'm trying to create a Pomodoro timer, however I've encountered a couple of issues.

  1. I run while loop in another function to update label with time left
  2. While in loop GUI unresponsive (that's OK)
  3. I've googled a little bit, went through some StackOverflow questions and I've found that this function running loop could be run in a separate thread.

This solved the first issue, but there is another

  1. Cancel button works, but I don't see the click animation.
  2. Timer does not stop immediately.

Can you kindly explain why this is happening and what I can do to increase the responsiveness of my GUI?

import time
import tkinter
import winsound
from tkinter.ttk import Combobox
import threading


def run_thread():
    global stop
    stop = False
    start_timer_btn.grab_release()
    threading.Thread(target=countdown()).start()


def stop_timer():
    global stop
    stop = True


def countdown():
    option = combo.get()
    if option == "25":
        seconds = 1500
    elif option == "15":
        seconds = 900
    # elif option == "2":
    #   countdown(300)
    # elif option == "3":
    #   countdown(1200)
    global stop
    while seconds and not stop:
        seconds -= 1
        print("minutes", seconds // 60, "seconds", seconds % 60)
        timer_label.configure(text=str(seconds // 60) + ":" + str(seconds % 60))
        window.update()
        time.sleep(1)
    winsound.PlaySound("SystemExit", winsound.SND_ALIAS)


stop = False
window = tkinter.Tk()
window.title("POMODORO Timer")
window.geometry("300x250")

combo = tkinter.ttk.Combobox(window)
combo["values"] = [25, 15]
combo.current(1)

timer_label = tkinter.Label(window, text="00:00")
start_timer_btn = tkinter.Button(window, text="Start timer", bg="green", fg="white", command=run_thread)
stop_timer_btn = tkinter.Button(window, text="Stop timer", bg="red", fg="white", command=stop_timer)

combo.grid(row=1, column=0)
start_timer_btn.grid(row=1, column=1)
stop_timer_btn.grid(row=1, column=2)
timer_label.grid(row=0, column=2, columnspan=3)

window.mainloop()
Tresdon
  • 1,211
  • 8
  • 18
Piosipov
  • 63
  • 5
  • Does this answer your question? [tkinter: how to use after method](https://stackoverflow.com/a/25753719/7414759) and [making-python-tkinter-label-widget-update](https://stackoverflow.com/questions/1918005) – stovfl Jun 18 '20 at 19:01

1 Answers1

0

It is generally discouraged to use sleep() especially when multithreading. Sleep waits 'at least' some time,but code execution may not restart due to other processes. This is what the documentation says:

Suspend execution of the calling thread for the given number of seconds. The argument may be a floating point number to indicate a more precise sleep time. The actual suspension time may be less than that requested because any caught signal will terminate the sleep() following execution of that signal’s catching routine. Also, the suspension time may be longer than requested by an arbitrary amount because of the scheduling of other activity in the system.

Changed in version 3.5: The function now sleeps at least secs even if the sleep is interrupted by a signal, except if the signal handler raises an exception (see PEP 475 for the rationale).

The alternative is to use the .after() method. Here are some links:

tkinter and time.sleep

How to create a timer using tkinter?

Ronald
  • 2,930
  • 2
  • 7
  • 18
  • Thanks I will go through the articles and get back :) P.S. from what you've cited seems indeed its not a good idea to use sleep. – Piosipov Jun 18 '20 at 19:27
  • Nice job, thumbs up. – Ronald Jun 19 '20 at 18:15
  • if you dont mind can i ask 1 more thing please? why if i call threading.Thread(target=playsound).start() - it works, but if i call it like this:threading.Thread(target=playsound()).start() - seems that it indefinitely loops? If i call timer function like this it just roll down to 0 instantly and throws and error that maximum iteration depth reached or something like this. Thanks in advance ! – Piosipov Jun 19 '20 at 19:20
  • I never worked with threading, but this seems syntactical: `target=func` versus `target=func()`. In the first example the function is *passed* where in the second example the function is *called*. See also the doc: https://docs.python.org/3/library/threading.html#threading.Thread – Ronald Jun 19 '20 at 19:27