Poom1997
Poom1997

Reputation: 47

Python Tkinter Stopwatch Error

i have made a double countdown timer with python and tkinter but it seemed that it cannot be run if the tkinter window is not on the foreground and it cannot simultaneously run. This is my code:

import tkinter as tk
import tkinter.ttk as ttk
import time
class app:

 def __init__(self):
   self = 0

 def mainGUIArea():
    def count_down_1():
        for i in range(79, -1, -1):
            timeCount = "{:02d}:{:02d}".format(*divmod(i, 60))
            time_str.set(timeCount)
            root.update()
            time.sleep(1)

    def count_down_2():
        for j in range(10, -1, -1):
            timeCount = "{:02d}:{:02d}".format(*divmod(j, 60))
            time_str1.set(timeCount)
            root.update()
            time.sleep(1)

    #Main Window
    root = tk.Tk()
    root.title("Super Timer V1.0")
    root.minsize(300,300)
    root.geometry("500x300")

    #Timer1
    time_str = tk.StringVar()
    label_font = ('Courier New', 40)
    tk.Label(root, textvariable = time_str, font = label_font, bg = 'white', fg = 'blue', relief = 'raised', bd=3).pack(fill='x', padx=5, pady=5)
    tk.Button(root, text=' Start Timer! ',bg='lightgreen',fg='black', command=count_down_1).pack()
    tk.Button(root, text='Stop and Exit',bg='red',fg='white', command=root.destroy).pack()

    #Timer2
    time_str1 = tk.StringVar()
    label_font = ('Courier New', 40)
    tk.Label(root, textvariable = time_str1, font = label_font, bg = 'white', fg='blue', relief='raised', bd=3).pack(fill='x', padx=5, pady=5)
    tk.Button(root, text=' Start Timer! ',bg='lightblue',fg='black', command=count_down_2).pack()
    tk.Button(root, text='Stop and Exit',bg='red',fg='white', command=root.destroy).pack()


def main():
  app.mainGUIArea()

main()

Do you have any suggestion? Thank You :)

Upvotes: 1

Views: 665

Answers (2)

Surya
Surya

Reputation: 11

This is a simple tkinter(gui) stopwatch I made which works perfectly. Check it out

__author__ = 'Surya'
from tkinter import *
import time
class StopWatch(Frame):
    def __init__(self, parent = None, ** kw):
        Frame.__init__(self, parent, kw)
        self._timeelapsed = 0.0
        self._start = 0.0
        self._run = 0
        self.timestr = StringVar()
        self.makeWidgets()
    def makeWidgets(self):
        l = Label(self, textvariable=self.timestr)
        self._setTime(self._timeelapsed)
        l.pack(fill=X, expand=NO, pady=2, padx=2)

    def _update(self):
        self._timeelapsed = time.time() - self._start
        self._setTime(self._timeelapsed)
        self._timer = self.after(50, self._update)

    def _setTime(self, elap):
        minutes = int(elap/60)
        seconds = int(elap - minutes*60.0)
        hseconds = int((elap - minutes*60.0 - seconds)*100)
        self.timestr.set('%02d:%02d:%02d' % (minutes, seconds, hseconds))

    def Start(self):
        if not self._run:
            self._start = time.time() - self._timeelapsed
            self._update()
            self._run = 1

    def Stop(self):
        if self._run:
            self.after_cancel(self._timer)
            self._timeelapsed = time.time() - self._start
            self._setTime(self._timeelapsed)
            self._run = 0

    def Reset(self):
        self._start = time.time()
        self._timeelapsed = 0.0
        self._setTime(self._timeelapsed)


def main():
    root = Tk()
    sw = StopWatch(root)
    sw.pack(side=TOP)
    Button(root, text='Start!', command=sw.Start).pack(side=LEFT)
    Button(root, text='Stop!', command=sw.Stop).pack(side=LEFT)
    Button(root, text='Reset!!!', command=sw.Reset).pack(side=LEFT)
    Button(root, text='Quit!!!', command=root.quit).pack(side=LEFT)

    root.mainloop()

if __name__ == '__main__':
        main()

Upvotes: 1

Bryan Oakley
Bryan Oakley

Reputation: 385960

The calls to time.sleep are at least part of the problem. When you call sleep it does literally that -- it puts the application to sleep. No events can be processed and the GUI freezes. This is the wrong way to do a countdown timer.

The other problem is the calls to update inside the loops alongside the calls to time.sleep. This call will process events, which means that when one of the loops is running and you click a button, you may end up calling the other function, interleaving your two loops.

The proper way to do something periodically is to use after to repeatedly call a function. The general pattern is this:

def update_display(self):
    <do whatever code you want to update the display>
    root.after(1000, self.update_display)

You can have as many of these running in parallel that you want (up to practical limits, obviously), and your GUI will be completely responsive between updates.

Here's a quick example:

class Countdown(tk.Label):
    def __init__(self, parent):
        tk.Label.__init__(self, parent, width=5, text="00:00")
        self.value = 0
        self._job_id = None

    def tick(self):
        self.value -= 1
        text = "{:02d}:{:02d}".format(*divmod(self.value, 60))
        self.configure(text=text)
        if self.value > 0:
            self._job_id = self.after(1000, self.tick)

    def start(self, starting_value=60):
        if self._job_id is not None: return

        self.value = starting_value
        self.stop_requested = False
        self.after(1000, self.tick)

    def stop(self):
        self.after_cancel(self._job_id)
        self._job_id = None

Upvotes: 1

Related Questions