How do I make the countdown display in a countdown timer - python

I am new to Python and just programming in general. I need help to create a countdown display to my timer which is a tequila timer. My friends drink a lot of Tequila and asked me to make them a timer, because they know nothing about computers.
Anyways, I have made the layout the best way I could do it and it looks like this: https://i.stack.imgur.com/bvt57.png
What I need help with is that when the 1 minute button is clicked, it counts down from one minute to 0. And the same goes with the other button. I also need help with the stop ruction, so you can stop the timer at any time by pressing the stop button. It would really be a big help!!
Here is my code:
import tkinter as tk
#GUI
root = tk.Tk()
root.title("Tequila timer")
#Load of background with the tequila bottle
canvas = tk.Canvas(root, width=423, height=700)
canvas.pack()
Load = tk.PhotoImage(file="tequila.png")
canvas.create_image(211, 350, image=Load)
#buttons
btn_1min = tk.Button(root, text="1 min", width=10, height=5, command=root.quit)
btn_1min_v = canvas.create_window(140, 350, window=btn_1min)
btn_10min = tk.Button(root, text="10 min", width=10, height=5, command=root.quit)
bt1_10min_v = canvas.create_window(283, 350, window=btn_10min)
btn_1hour = tk.Button(root, text="1 hour", width=10, height=5, command=root.quit)
bt1_1hour_v = canvas.create_window(140, 475, window=btn_1hour)
btn_2hours = tk.Button(root, text="2 hours", width=10, height=5, command=root.quit)
bt1_2hours_v = canvas.create_window(283, 475, window=btn_2hours)
btn_stop = tk.Button(root, text="Stop", width=10, height=5, command=root.quit)
bt1_stop_v = canvas.create_window(211, 600, window=btn_stop)
#Display
label = tk.Label(root, text="00:00:00", width=9, font=("calibri", 40, "bold"))
label.pack()
label_v = canvas.create_window(211, 200, window=label)
root.mainloop()

The problem is none of your buttons are mapped to anything. Well, other than root.quit which just closes your window.
So lets start with your first problem. You need to update the text in your Label. A good way to do that is with tkinters StringVar class. Updating a StringVar will also update the text in your Label so lets do that first.
countdown = tk.StringVar()
countdown.set("00:00:00")
and then set it as the text in your Label.
label = tk.Label(root, textvariable=countdown, width=9, font=("calibri", 40, "bold"))
Now that that's taken care of. lets move on to your second problem. Your lack of call back functions. First lets set us up with a function to call that accepts a variable amount of seconds to count down from.
To display something after root.mainloop() has been called we can use the after() method. It will return an identifier that we can use to cancel the update later. However, in order to use it in another function later to cancel, we should make it a global variable with the keyword global.
The root.after takes a milliseconds argument and a function to call after those milliseconds have elapsed. Lets call this function again with one fewer milliseconds than it was called with. also, if the seconds have dipped below 0 we should cancel the callback.
We can do that with root.after_cancel and handing it the after identifier we retrieved from root.after before.
Another thing to note is that root.afters function argument expects a function object. To hand it a function with an argument we can wrap it in a lambda.
def update(seconds):
global after
if seconds >= 0:
countdown.set(seconds_to_time(seconds))
after = root.after(1000, lambda: update(seconds - 1))
else:
root.after_cancel(after)
But what is this seconds_to_time function were setting our StringVar to? Well, it's just a little helper to make our seconds display in a proper hh:mm:ss format.
def seconds_to_time(seconds):
hours = seconds // 3600
seconds -= hours * 3600
minutes = seconds // 60
seconds -= minutes * 60
return f'{hours:02d}:{minutes:02d}:{seconds:02d}'
And your final request is to stop the countdown. We can do that by just cancelling our global after identifier. It's a good idea to make sure we've set the after first by avoiding NameError errors in case it hasn't been set yet.
def stop():
try:
root.after_cancel(after)
except NameError:
pass
Full code below:
note the callback functions for your buttons are wrapped in lambdas to allow us to hand an argument to the callback.
import tkinter as tk
def update(seconds):
global after
if seconds >= 0:
countdown.set(seconds_to_time(seconds))
after = root.after(1000, lambda: update(seconds - 1))
else:
root.after_cancel(after)
def seconds_to_time(seconds):
hours = seconds // 3600
seconds -= hours * 3600
minutes = seconds // 60
seconds -= minutes * 60
return f'{hours:02d}:{minutes:02d}:{seconds:02d}'
def stop():
try:
root.after_cancel(after)
except NameError:
pass
#GUI
root = tk.Tk()
root.title("Tequila timer")
#Load of background with the tequila bottle
canvas = tk.Canvas(root, width=423, height=700)
canvas.pack()
Load = tk.PhotoImage(file="tequila.png")
canvas.create_image(211, 350, image=Load)
countdown = tk.StringVar()
countdown.set("00:00:00")
#buttons
btn_1min = tk.Button(root, text="1 min", width=10, height=5, command=lambda: update(60))
btn_1min_v = canvas.create_window(140, 350, window=btn_1min)
btn_10min = tk.Button(root, text="10 min", width=10, height=5, command=lambda: update(600))
bt1_10min_v = canvas.create_window(283, 350, window=btn_10min)
btn_1hour = tk.Button(root, text="1 hour", width=10, height=5, command=lambda: update(3600))
bt1_1hour_v = canvas.create_window(140, 475, window=btn_1hour)
btn_2hours = tk.Button(root, text="2 hours", width=10, height=5, command=lambda: update(7200))
bt1_2hours_v = canvas.create_window(283, 475, window=btn_2hours)
btn_stop = tk.Button(root, text="Stop", width=10, height=5, command=stop)
bt1_stop_v = canvas.create_window(211, 600, window=btn_stop)
#Display
label = tk.Label(root, textvariable=countdown, width=9, font=("calibri", 40, "bold"))
label.pack()
label_v = canvas.create_window(211, 200, window=label)
root.mainloop()

Related

Only open 1 window when button clicked multiple times

I am trying to create a basic invoicing system. However i have encountered an issue as you can tell from my the title, is there any way to achieve this. I have been using a counter to determine if the window should open or not but i dont think it is right.
from tkinter import *
window = Tk()
count = 0
def openNewWindow():
global count
count = count + 1
if count == 1:
newWindow = Toplevel(window)
newWindow.title("New Window")
newWindow.geometry("800x800")
newWindow.title('test ©') # Frame title
newWindow.iconbitmap('icon4.ico') # Frame logo
if 'normal' == newWindow.state():
count = 2
else:
count = 0
width = window.winfo_screenwidth()
height = window.winfo_screenheight()
window.geometry("%dx%d" % (width, height))
bg = PhotoImage(file="bsor.gif")
label_image = Label(window, image=bg)
label_image.place(x=0, y=0)
title_label = Label(window, text="Job Management System", bg="black", fg="white")
title_label.config(font=("Courier", 70))
title_label.place(x=65, y=3)
customer_database_button = Button(window, text="Customer Database", width="23", height="2",
font=('Courier', 13, 'bold'), command=openNewWindow)
customer_database_button.grid(row=3, column=0, pady=185, padx=(110, 0))
employee_database_button = Button(window, text="Employee Database", width="23", height="2",
font=('Courier', 13, 'bold'))
employee_database_button.grid(row=3, column=1, pady=10, padx=(50, 0))
job_category_button = Button(window, text="Job Category (Pricing)", width="23", height="2",
font=('Courier', 13, 'bold'))
job_category_button.grid(row=3, column=2, pady=10, padx=(50, 0))
quote_sale_button = Button(window, text="Quotes / Sales", width="23", height="2", font=
('Courier', 13, 'bold'))
quote_sale_button.grid(row=3, column=3, pady=10, padx=(50, 0))
cash_management_button = Button(window, text="Cash Management", width="23", height="2", font=
('Courier', 13, 'bold'))
cash_management_button.grid(row=3, column=4, pady=10, padx=(50, 0))
analysis_mode_button = Button(window, text="Analysis Mode", width="23", height="2", font=
('Courier', 13, 'bold'))
analysis_mode_button.grid(row=3, column=5, pady=10, padx=(50, 0))
window.title('test') # Frame title
window.iconbitmap('icon4.ico') # Frame logo
window.mainloop()
Here is a minimal example on how to do it (works best with only one additional allowed window):
from tkinter import Tk, Toplevel, Button
def open_window(button):
button.config(state='disabled')
top = Toplevel(root)
top.transient(root)
top.focus_set()
top.bind('<Destroy>', lambda _: btn.config(state='normal'))
root = Tk()
root.geometry('300x200')
btn = Button(root, text='Open new window!', command=lambda: open_window(btn))
btn.pack(expand=True)
root.mainloop()
Just have the function disable the button and bind a <Destroy> event to the Toplevel to set the button's state back to normal. (Also you may want to use .transient on the Toplevel to make it appear above its master so that people don't forget that they haven't closed the window and wonder why they can't press the button (it will also not display additional icon in the taskbar))
Also:
I strongly advise against using wildcard (*) when importing something, You should either import what You need, e.g. from module import Class1, func_1, var_2 and so on or import the whole module: import module then You can also use an alias: import module as md or sth like that, the point is that don't import everything unless You actually know what You are doing; name clashes are the issue.
I strongly suggest following PEP 8 - Style Guide for Python Code. Function and variable names should be in snake_case, class names in CapitalCase. Don't have space around = if it is used as a part of keyword argument (func(arg='value')) but have space around = if it is used for assigning a value (variable = 'some value'). Have space around operators (+-/ etc.: value = x + y(except here value += x + y)). Have two blank lines around function and class declarations.

after_cancel doesn't work in python tkinter

I made a simple countdown in my trivia game and every time that I go to the next slide it doesn't cancel the previous countdown timer and overlaps with the previous one.
I searched about that on the web and found the after_cancel function that cancels the timer after I go to the next slide and then I recreate it. But it still overlaps even after I added this function.
I think that I didn't give the after_cancel the correct arguments.
from tkinter import *
window = Tk()
window.attributes('-fullscreen',True)
WaitState = StringVar()
count = 10
button_label = StringVar()
def clear():
WaitState.set(1)
for widgets in window.winfo_children():
widgets.destroy()
def clear_time():
clear()
time_up_label = Label(
window,
text="Time is up",
bg = "#6378ff",
fg="#000000",
font=("Arial", 100,"bold"))
time_up_label.place(relx = 0.5,rely = 0.5,anchor = 'center')
continue_but = Button(
window,
text="Continue",
font=("Knewave",25,"bold"),
bg="#942222",
width=11,
height=5,
command=clear)
continue_but.place(relx = 1.0,rely = 1.0,anchor =SE)
continue_but.wait_variable(WaitState)
def button_countdown(i, label):
if i > 0:
i -= 1
label.set(i)
window.after_id = window.after(1000, lambda: button_countdown(i, label))
else:
window.after_cancel(window.after_id)
clear_time()
for index in range(5):
continue_but = Button(
window,
text="Continue",
font=("Knewave",25,"bold"),
bg="#942222",
width=11,
height=5,
command=clear)
continue_but.place(relx = 1.0,rely = 1.0,anchor =SE)
button_label.set(count)
timer_label = Label(
window,
textvariable=button_label,
bg="#6378ff",
fg="#ff0000",
font=("Arial",46,"bold"))
timer_label.pack()
button_countdown(count, button_label)
continue_but.wait_variable(WaitState)
window.mainloop()

How to make a Tkinter window not resizable?

I need a Python script that uses the Tkinter module to create a static (not resizable) window.
I have a pretty simple Tkinter script but I don't want it to be resizable. How do I prevent a Tkinter window from being resizable? I honestly don't know what to do.
This is my script:
from tkinter import *
import ctypes, os
def callback():
active.set(False)
quitButton.destroy()
JustGo = Button(root, text=" Keep Going!", command= lambda: KeepGoing())
JustGo.pack()
JustGo.place(x=150, y=110)
#root.destroy() # Uncomment this to close the window
def sleep():
if not active.get(): return
root.after(1000, sleep)
timeLeft.set(timeLeft.get()-1)
timeOutLabel['text'] = "Time Left: " + str(timeLeft.get()) #Update the label
if timeLeft.get() == 0: #sleep if timeLeft = 0
os.system("Powercfg -H OFF")
os.system("rundll32.exe powrprof.dll,SetSuspendState 0,1,0")
def KeepGoing():
active.set(True)
sleep()
quitButton1 = Button(root, text="do not sleep!", command=callback)
quitButton1.pack()
quitButton1.place(x=150, y=110)
root = Tk()
root.geometry("400x268")
root.title("Alert")
root.configure(background='light blue')
timeLeft = IntVar()
timeLeft.set(10) # Time in seconds until shutdown
active = BooleanVar()
active.set(True) # Something to show us that countdown is still going.
label = Label(root, text="ALERT this device will go to sleep soon!", fg="red")
label.config(font=("Courier", 12))
label.configure(background='light blue')
label.pack()
timeOutLabel = Label(root, text = 'Time left: ' + str(timeLeft.get()), background='light blue') # Label to show how much time we have left.
timeOutLabel.pack()
quitButton = Button(root, text="do not sleep!", command=callback)
quitButton.pack()
quitButton.place(x=150, y=110)
root.after(0, sleep)
root.mainloop()
The resizable method on the root window takes two boolean parameters to describe whether the window is resizable in the X and Y direction. To make it completely fixed in size, set both parameters to False:
root.resizable(False, False)

How to display the value of function in tkinter?

I am new to python and tkinter and I have decided that I will make a stopwatch.
I have gooled alot and find many useful information, but I still haven't found how to display value of a function in tkinter. Here is my current code:
import time
from tkinter import*
import os
root = Tk()
def clock(event):
second = 0
minute = 0
hour = 0
while True:
time.sleep(0.99)
second +=1
print(hour,":",minute,":",second)
return
def stop(event):
time.sleep(1500)
def clear(event):
os.system('cls')
button1 = Button(root, text="Start")
button2 = Button(root, text="Stop")
button3 = Button(root, text="Clear")
button1.bind("<Button-1>", clock)
button2.bind("<Button-1>", stop)
button3.bind("<Button-1>", clear)
button1.grid(row=2, column=0, columnspan=2)
button2.grid(row=2, column=2, columnspan=2)
button3.grid(row=2, column=4, columnspan=2)
root.mainloop()
I am aware that the code isn't perefect yet(especially the functions stop and clear).
You might consider using callback functions (i.e. call to your function when something happens — when clicking a button for example):
Quoting portions of Tkinter Callbacks:
In Tkinter, a callback is Python code that is called by Tk when
something happens. For example, the Button widget provides a command
callback which is called when the user clicks the button. You also use
callbacks with event bindings.
You can use any callable Python object as a callback. This includes
ordinary functions, bound methods, lambda expressions, and callable
objects. This document discusses each of these alternatives briefly.
...
To use a function object as a callback, pass it directly to Tkinter.
from Tkinter import *
def callback():
print "clicked!"
b = Button(text="click me", command=callback)
b.pack()
mainloop()
It's unclear from your sample code which function's value you want to display.
Regardless, a good way to do accomplish something like that in tkinter is by creating instances of its StringVar Variable class and then specifying them as the textvariable option of another widget. After this is done, any changes to the value of the StringVar instance will automatically update the associated widget's text.
The code below illustrates this:
import os
import time
import tkinter as tk
class TimerApp(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master=None)
self.grid()
self.create_widgets()
self.elapsed = 0
self.refresh_timer()
self.after_id = None # used to determine and control if timer is running
def create_widgets(self):
self.timer = tk.StringVar()
self.timer.set('')
self.timer_label = tk.Label(self, textvariable=self.timer)
self.timer_label.grid(row=1, column=2)
self.button1 = tk.Button(self, text="Start", command=self.start_clock)
self.button1.grid(row=2, column=0, columnspan=2)
self.button2 = tk.Button(self, text="Stop", command=self.stop_clock)
self.button2.grid(row=2, column=2, columnspan=2)
self.button3 = tk.Button(self, text="Clear", command=self.clear_clock)
self.button3.grid(row=2, column=4, columnspan=2)
def start_clock(self):
self.start_time = time.time()
self.after_id = self.after(1000, self.update_clock)
def stop_clock(self):
if self.after_id:
self.after_cancel(self.after_id)
self.after_id = None
def clear_clock(self):
was_running = True if self.after_id else False
self.stop_clock()
self.elapsed = 0
self.refresh_timer()
if was_running:
self.start_clock()
def update_clock(self):
if self.after_id:
now = time.time()
delta_time = round(now - self.start_time)
self.start_time = now
self.elapsed += delta_time
self.refresh_timer()
self.after_id = self.after(1000, self.update_clock) # keep updating
def refresh_timer(self):
hours, remainder = divmod(self.elapsed, 3600)
minutes, seconds = divmod(remainder, 60)
self.timer.set('{:02d}:{:02d}:{:02d}'.format(hours, minutes, seconds))
app = TimerApp()
app.master.title('Timer')
app.mainloop()

How do I create an automatically updating GUI using Tkinter?

from Tkinter import *
import time
#Tkinter stuff
class App(object):
def __init__(self):
self.root = Tk()
self.labeltitle = Label(root, text="", fg="black", font="Helvetica 40 underline bold")
self.labeltitle.pack()
self.labelstep = Label(root, text="", fg="black", font="Helvetica 30 bold")
self.labelstep.pack()
self.labeldesc = Label(root, text="", fg="black", font="Helvetica 30 bold")
self.labeldesc.pack()
self.labeltime = Label(root, text="", fg="black", font="Helvetica 70")
self.labeltime.pack()
self.labelweight = Label(root, text="", fg="black", font="Helvetica 25")
self.labelweight.pack()
self.labelspeed = Label(root, text="", fg="black", font="Helvetica 20")
self.labelspeed.pack()
self.labeltemp = Label(root, text="", fg="black", font="Helvetica 20")
self.labeltemp.pack()
self.button = Button(root, text='Close recipe', width=25, command=root.destroy)
self.button.pack()
def Update(self, label, change):
label.config(text=str(change))
def main():
app = App()
app.mainloop()
if __name__ == "__main__":
main()
I'm trying to create a recipe display which will show the step, instructions, weight and other variables on a screen in a Tkinter GUI.
However, I do not know how to update the GUI to change with each new step of the recipe, as the content has to be dynamically updated based on user input (taken from a server). How can I achieve updating of the GUI's other elements based on the change in steps?
You can use after() to run function after (for example) 1000 miliseconds (1 second) to do something and update text on labels. This function can run itself after 1000 miliseconds again (and again).
It is example with current time
from Tkinter import *
import datetime
root = Tk()
lab = Label(root)
lab.pack()
def clock():
time = datetime.datetime.now().strftime("Time: %H:%M:%S")
lab.config(text=time)
#lab['text'] = time
root.after(1000, clock) # run itself again after 1000 ms
# run first time
clock()
root.mainloop()
BTW: you could use StringVar as sundar nataraj Сундар suggested
EDIT: (2022.01.01)
Updated to Python 3 with other changes suggested by PEP 8 -- Style Guide for Python Code
import tkinter as tk # PEP8: `import *` is not preferred
import datetime
# --- functions ---
# PEP8: all functions before main code
# PEP8: `lower_case_name` for funcitons
# PEP8: verb as function's name
def update_clock():
# get current time as text
current_time = datetime.datetime.now().strftime("Time: %H:%M:%S")
# udpate text in Label
lab.config(text=current_time)
#lab['text'] = current_time
# run itself again after 1000 ms
root.after(1000, update_clock)
# --- main ---
root = tk.Tk()
lab = tk.Label(root)
lab.pack()
# run first time at once
update_clock()
# run furst time after 1000ms (1s)
#root.after(1000, update_clock)
root.mainloop()
if you want to change label dynamically
self.dynamiclabel=StringVar()
self.labeltitle = Label(root, text=self.dynamiclabel, fg="black", font="Helvetica 40 underline bold")
self.dyanamiclabel.set("this label updates upon change")
self.labeltitle.pack()
when ever you get new value then just use .set()
self.dyanamiclabel.set("Hurrray! i got changed")
this apply to all the labels.To know more read this docs
If you are using labels, then you can use this:
label = tk.Label(self.frame, bg="green", text="something")
label.place(rely=0, relx=0.05, relwidth=0.9, relheight=0.15)
refresh = tk.Button(frame, bg="white", text="Refreshbutton",command=change_text)
refresh.pack(rely=0, relx=0.05, relwidth=0.9, relheight=0.15)
def change_text()
label["text"] = "something else"
Works fine for me, but it is dependent on the need of a button press.
I added a process bar in my window, and change its value according to randint for every 1 second using the update function:
from random import randint
def update():
mpb["value"] = randint(0, 100) # take process bar for example
window.after(1000, update)
update()
window.mainloop()
I wrote an example with Python 3.7
from tkinter import *
def firstFrame(window):
global first_frame
first_frame = Frame(window)
first_frame.place(in_=window, anchor="c", relx=.5, rely=.5)
Label(first_frame, text="ATTENTION !").grid(row=1,column=1,columnspan=3)
def secondFrame(window):
global second_frame
second_frame= Frame(window, highlightbackground=color_green, highlightcolor=color_green, highlightthickness=3)
second_frame.place(in_=window, anchor="c", relx=.5, rely=.5)
Label(second_frame, text="This is second frame.").grid(row=1, column=1, columnspan=3, padx=25, pady=(15, 0))
window = Tk()
window.title('Some Title')
window.attributes("-fullscreen", False)
window.resizable(width=True, height=True)
window.geometry('300x200')
firstFrame(window)
secondFrame(window)
first_frame.tkraise()
window.after(5000, lambda: first_frame.destroy()) # you can try different things here
window.mainloop()
Use root.config() and add a way to run

Categories

Resources