h4kr
h4kr

Reputation: 248

Attempt to make a timer in python, where is the error?

I'm quite new to python programming and I was attempting to create a countdown timer. I wrote down the following code for the timer but it's not working. Can you help in figuring out where the error is? (I did a bit of research on this site and found a few questions relating to timers. In fact most of this code is written based on another answer but I did some changes to it because that code was for a clock and I'm trying to make a timer.)

from tkinter import *
from tkinter import messagebox

class alarmclock():
    
    def __init__(self):
        self.alarm = Tk()
        self.l0 = Label(self.alarm, text = '----------- Alarm clock 1 -----------')
        self.l1 = Label(self.alarm, text = 'Please type in the time:')
        self.e0 = Entry(self.alarm)
        self.l2 = Label(self.alarm)
        self.b0 = Button(self.alarm, text = 'Start countdown', command = lambda: self.timer)
        self.l0.grid(row = 0, column = 0, columnspan = 2)
        self.l1.grid(row = 1, column = 0, padx = 10, pady =5)
        self.e0.grid(row = 1, column = 1, padx = 10)
        self.l2.grid(row = 2, column = 1, padx = 10)
        self.b0.grid(row = 3, column = 0, columnspan = 2, pady = 5)
        self.alarm.mainloop()
    
    def timer(self):
        self.b = float(self.e0.get())
        self.l2.configure(text = str(self.b))
        self.b-=1
        if self.b != 0:
            self.alarm.after(1000, self.timer)
        if self.b == 0:
            self.l2.configure(text = str(self.b))
            self.messagebox.showwarning('Alarm Clock', 'Time\'s up!')

Upvotes: 0

Views: 267

Answers (3)

Shivang Srivastava
Shivang Srivastava

Reputation: 11

try my timer:

import time

from tkinter import messagebox
class PDTkClock(Exception):
__module__ = Exception.__module__

class Timer:
"""
valid parameter ::--
  year, month, week, day, hour, minute, second

"""
_value = ("year", 'month', "week", "day", "hour", "minute", "second")

def __init__(self, tk_widget, **kwargs):
    self._sec = 0
    self.time_over = False
    if self._check(**kwargs) and not self.time_over:
        for i in kwargs:
            if i == "year":
                val = kwargs[i]
                self._sec += val * 31556952
            elif i == "month":
                val = kwargs[i]
                self._sec += val * 2628000
            elif i == "week":
                val = kwargs[i]
                self._sec += val * 604800
            elif i == "day":
                val = kwargs[i]
                self._sec += val * 86400
            elif i == "hour":
                val = kwargs[i]
                self._sec += val * 3600
            elif i == "minute":
                val = kwargs[i]
                self._sec += val * 60
            elif i == "second":
                val = kwargs[i]
                self._sec += val * 1
        self._time_it(tk_widget, self._sec)
    else:
        raise PDTkClock(f"kwargs must be {self._value}")

def _time_it(self, tk_widget, sec):
    _ = sec

    year = sec // 31556952
    sec -= year * 31556952

    month = sec // 2628000
    sec -= month * 2628000

    week = sec // 604800
    sec -= week * 604800

    day = sec // 86400
    sec -= day * 86400

    hour = sec // 3600
    sec -= hour * 3600

    minute = sec // 60
    sec -= minute * 60

    second = sec // 1
    get = f"{year}:{month}:{week}:{day}:{hour}:{minute}:{second}"
    if _ >=0 and not self.time_over:
        tk_widget['text'] = get
        tk_widget.update()
        tk_widget.after(1000, self._time_it, tk_widget, _ - 1)
    else:
        messagebox.showwarning('Alarm Clock', 'Time\'s up!')

def _check(self, **kwargs):
    ch = []
    for i in kwargs:
        if i in self._value:
            ch.append(True)
        else:
            ch.append(False)
            break
    return all(ch)
from tkinter import Tk, Label

r = Tk()

l1 = Label(r)
l1.pack()
Timer(l2, second=10, minute=0)
r.mainloop()

Upvotes: 1

Delrius Euphoria
Delrius Euphoria

Reputation: 15098

Firstly to answer your main question: tkinter requires a function that it can call if you press the button, so you have to supply the function so tkinter can call it, here you are are supplying a function with lambda that returns the function, you can want to call. So when you press the button the lambda function is called and the function(you wanted to call) is returned, but not called. TL;DR, use:

command = lambda: self.timer()

Secondly, the logic is flawed because you are setting the time to self.b each time the function is ran, instead pass in an argument:

self.b0 = Button(self.alarm,....,command=lambda: self.timer(self.e0.get()))

def timer(self, time):
    time = float(time)
    self.l2.configure(text=time)

    if time > 0:
        time -= 1
        self.alarm.after(1000, self.timer, time)
    else:
        self.l2.configure(text=time)
        messagebox.showwarning('Alarm Clock', 'Time\'s up!')

Upvotes: 2

ALai
ALai

Reputation: 799

What about something like this?

from tkinter import *
from tkinter import messagebox

class alarmclock():
    
    def __init__(self):
        self.alarm = Tk()
        self.l0 = Label(self.alarm, text = '----------- Alarm clock 1 -----------')
        self.l1 = Label(self.alarm, text = 'Please type in the time:')
        self.e0 = Entry(self.alarm)
        self.countdown = StringVar(self.alarm, "")
        self.l2 = Label(self.alarm, textvariable=self.countdown)
        self.b0 = Button(self.alarm, text = 'Start countdown', command = lambda : self.timer(reset=True))
        self.l0.grid(row = 0, column = 0, columnspan = 2)
        self.l1.grid(row = 1, column = 0, padx = 10, pady =5)
        self.e0.grid(row = 1, column = 1, padx = 10)
        self.l2.grid(row = 2, column = 1, padx = 10)
        self.b0.grid(row = 3, column = 0, columnspan = 2, pady = 5)
        self.alarm.mainloop()
    
    def timer(self, reset=False):
        if reset:
            self.b = float(self.e0.get())
        self.countdown.set(str(self.b))
        self.b-=1
        if self.b >= 0:
            self.alarm.after(1000, self.timer)
        else:
            messagebox.showwarning('Alarm Clock', 'Time\'s up!')

if __name__ == '__main__':
    my_alarm = alarmclock()

Notice that:

  • you were not actually calling the timer function due to an error in your lambda function definition
  • you were resetting the countdown value at each update
  • checking for b==0 after having decreased b caused the timer to not display the "0"
  • using self.messagebox.showwarning(_) throws an error AttributeError: 'alarmclock' object has no attribute 'messagebox'; messagebox.showwarning(_) works instead
  • I have used a StringVar element for convenience (not necessary)

By the way, I suggest you to NOT import modules using *. Try using import tkinter as tk instead.

Upvotes: 5

Related Questions