time delay using 'after' method in tkinter works independently - python

Consider this example
from tkinter import *
history_text_list=[]
window=Tk()
window.geometry("1366x768+1+1")
winner_lbl=Label(window, text="0", fg='red', font=("Comic Sans MS", 96))
winner_lbl.place(x=5, y=5)
a_pixel = PhotoImage(width=1, height=1)
next_btn=Button(window, text="Next", font=("Comic Sans MS", 32), fg='blue', image=a_pixel, height = 70 , width = 130,compound="c")
next_btn.place(x=235, y=70)
history_text = StringVar()
history_label = Label(window, textvariable=history_text, wraplength=850, font=("Comic Sans MS", 16),justify="left",anchor="nw")
history_label.place(x=410, y=5)
def countdown(list_of_numbers):
print('in function list-'+str(list_of_numbers))
winner_lbl['fg'] = 'black'
winner_lbl['text'] = list_of_numbers[0]
list_of_numbers.pop(0)
if list_of_numbers:
window.after(500,countdown,list_of_numbers)
else:
winner_lbl['fg'] = 'red'
return
def MyButtonClicked(self):
one_to_hundred=list(range(1,10))
countdown(one_to_hundred)
history_text_list.append(10)
history_text.set(str(history_text_list))
next_btn.bind('<Button-1>', MyButtonClicked)
window.mainloop()
Here, on pressing next button you will notice that label '0' increments to 9 but 10 gets added in history area quickly though it should happen after countdown function has ended .
What should I do to ensure serial execution ?
Thanks.

The after method returns right away. It does not wait for the timeout to expire. You should place the history_text.set call in your countdown function when the list of numbers is empty.
By the way, you can use if list_of_numbers instead of if len(list_of_numbers)==0 to check if the list is empty.

Related

How to clear an entry () in tkinter from python

In my program, I want to delete the input introduced in the entry() pressing a button that is call "Delete input"
I define a function called SendData(), and I try to use: Entryy.clipboard_clear, and when I test it, it doesn´t do anything.
By the way, this is all of my code:
from tkinter import* #importar el tkinter
App = Tk()
def SendData():
Entryy.clipboard_clear
App.title("HEYYY WASUP")
App.geometry("420x656")
App.config(bg="grey", cursor="cross", relief="sunken", bd="10")
Entryy=Entry()
Entryy.grid(row=0, column=2)
Entryy.config(width=25, font=("Comic sans ms", 10))
botonEnviar = Button(text="Delete input")
botonEnviar.grid(row=0, column=5)
botonEnviar.config(font=("Comic sans ms", 10), command= SendData)
App.mainloop()
Try this:
def SendData():
Entryy.delete(0, END)

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.

Is there some new keyword for return key in tkinter?

I am making a calculator and i bound the = button to return keypress but it is not working. I have bound my button to an equal function and also to click function
def equal():
if screenVal.get().isdigit():
value = int(screenVal.get())
else:
try:
value = eval(screenVal.get())
except Exception:
value = "Err"
screenVal.set(value)
screenEnt.update()
def click(event):
global screenVal
text = event.widget.cget("text")
if text == "=":
if screenVal.get().isdigit():
value = int(screenVal.get())
else:
try:
value = eval(screenVal.get())
except Exception:
value = "Err"
screenVal.set(value)
screenEnt.update()
elif text == "C":
screenVal.set("")
screenEnt.update()
else:
screenVal.set(screenVal.get() + text)
screenEnt.update()
b = Button(frame, text="=", padx=10, pady=10, font="consolas 15 bold", width=3, command=equal)
b.pack(side=LEFT)
b.bind('<Return>', click)
can anyone help?
I am working on python 3.7.9 on AMD 64
import tkinter as tk
root=tk.Tk()
def equal():
print('equal')
def click(event):
print('click')
b = tk.Button(root, text="=", padx=10, pady=10, font="consolas 15 bold", width=3, command=equal)
b.pack(side='left')
b.bind('<Return>', click)
root.mainloop()
If you run this script the equal command works as expected. What your problem seems to be is, that you cant execute the return binding. The event only happens if the keyboard-focus is on that button, try by pressing tab till the button have focus.
A solution could be to bind it to the frame or even better to the window. Like in the exampel below.
import tkinter as tk
root=tk.Tk()
def equal():
print('equal')
def click(event):
print('click')
b = tk.Button(root, text="=", padx=10, pady=10, font="consolas 15 bold", width=3, command=equal)
b.pack(side='left')
root.bind('<Return>', click)
root.mainloop()

How do I make the countdown display in a countdown timer

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()

Tkinter Radio Button Controlling If True

I am trying to do a simple program. I am trying to check if radio button 1 is selected show a button and if radio button 2 selected and if there is a button on the screen disappear it. Please help me.
from tkinter import *
from tkinter import messagebox
from tkinter import filedialog
bati = tkinter.Tk()
bati.geometry('500x500')
bati.title('Project')
def hello():
messagebox.showinfo("Say Hello", "Hello World")
def askfile():
bati.filename = filedialog.askopenfilename(initialdir = "/",title = "Select file",filetypes = (("jpeg files","*.jpg"),("all files","*.*")))
lb2 = Label(bati, text=bati.filename, fg='red', font=("Times", 10, "bold"))
lb2.place(x='270',y='34')
def first():
b = Button(bati, text='Import', activebackground='red', bd='3', bg='gray', fg='yellow', font=("Times New Roman", 10, "bold"), command=askfile)
b.place(x='200',y='30')
working = True
def second():
if working == True:
b.widget.pack_forget()
working = False
canvas_width = 3000
canvas_height = 220
w = Canvas(bati, width=canvas_width,height=canvas_height)
w.pack()
y = int(canvas_height / 2)
w.create_line(0, y, canvas_width, y, fill="#476042", width='2')
v = IntVar()
v.set('L')
rb1 = Radiobutton(bati, text='Import A File', value=1, variable=v, command=first, font=("Comic Sans MS", 10, "bold"))
rb2 = Radiobutton(bati, text='Choose From Web', value=2, variable=v, command=second, font=("Comic Sans MS", 10, "bold"))
rb1.place(x='50',y='30')
rb2.place(x='50',y='50')
working = False
bati.mainloop()
Issues:
Local variables will not work for this - you need to remember the button state outside of first and second, so that you can use it the next time.
We show the button with .place, so we should similarly hide it with .place_forget instead of .pack_forget.
The .place position should be given with integers instead of strings. Similarly for the button's bd, i.e. border width (in pixels, i.e. a number of pixels).
Event handlers are supposed to receive an event parameter, even if you ignore it. The .widget you've written in your second command is presumably copied from some other code that tries to find out the widget to hide from the event data (e.g. here). But that .widget would be the one that sent the command, i.e. the Radiobutton, not the Button that you want to hide.
What we will do is create the button ahead of time, in a global variable (in a more serious project, you should consider using classes instead, and then you would use a class member to remember it). Since the button should start out not visible, we just don't .place it right away, but instead use .place in first and .place_forget in second. So:
b = Button(
bati, text='Import', activebackground='red', bd=3,
bg='gray', fg='yellow', font=("Times New Roman", 10, "bold"),
command=askfile
)
def first(event):
b.place(x=200,y=30)
def second():
b.place_forget()

Categories

Resources