Why does my Tkinter toplevel freeze after a while? - python

I have stopwatch running on both my main window and my top level. The watch starts as soon as the program is launched and is stopped by closing the program. The toplevel is launched by using a button. The toplevel is supposed to display a copy of the stopwatch. But, when I keep the toplevel open for over an hour, it freezes. The main window runs fine. The toplevel works fine for another hour or so when I manually close and reopen it. Does anyone know what could be the problem? I'm running the code on Windows OS.
Here's the relevant code:
import datetime
import time
import tkinter as tk
time_elapse_flag = False
start_time = datetime.datetime.now()
diff = 0
def time_elapse():
global time_elapse_flag
global diff
if(not time_elapse_flag):
time_elapse_flag = True
time_elapse_count = tk.Label(window, text='', relief='sunken', bg='white', font='Helvetica 16 bold')
time_elapse_count.place(x=10, y=150, width=200, height=50)
start_time = datetime.datetime.now().replace(microsecond=0)
time_elapse_label = tk.Label(window, text='Recording data since:', font='Helvetica 12 bold')
time_elapse_label.place(x=10, y=120, width=200, height=30)
def update_time():
global start_time
global diff
current_time = datetime.datetime.now().replace(microsecond=0)
diff = current_time - start_time
time_elapse_count.config(text=diff)
time_elapse_label.after(1000,update_time)
update_time()
else:
time_elapse_flag = False
time_elapse_count = tk.Label(window, text='')
time_elapse_count.place(x=10, y=150, width=200, height=50)
time_elapse_label = tk.Label(window, text='')
time_elapse_label.place(x=10, y=120, width=200, height=30)
def Param_Window():
global time_elapse_flag
global diff
pw = tk.Toplevel()
pw.geometry('1200x550')
def update_values():
global diff
if(time_elapse_flag):
time_elapse_label = tk.Label(pw, text='Recording data since:', font='Helvetica 20 bold')
time_elapse_count = tk.Label(pw, text=diff, relief='sunken', bg='white', font='Helvetica 16 bold')
time_elapse_label.place(x=640, y=350, width=300, height=50)
time_elapse_count.place(x=640, y=400, width=200, height=50)
else:
time_elapse_label = tk.Label(pw, text='')
time_elapse_count = tk.Label(pw, text='')
time_elapse_label.place(x=640, y=350, width=300, height=50)
time_elapse_count.place(x=640, y=400, width=200, height=50)
pw.after(800,update_values)
update_values()
if __name__ == "__main__":
window = tk.Tk()
window.geometry('550x250')
disp_param_button = tk.Button(window, text='Display Parameters', command=Param_Window, bg='sky blue', font='Helvetica 12 bold', width=20, state='normal', relief='raised')
disp_param_button.place(x=300, y=150)
print('Automatically starting in')
for cd in range(5):
print(5-cd)
time.sleep(1)
time_elapse()
window.mainloop( )

The problem is that you're creating two new label widgets every 800 milliseconds in Param_Window. When you run for an hour, that means you will have created 4,500 label widgets. At some point you're going to hit a hard limit, because you can't create an infinite number of widgets.
You need to restructure your code so that you create the labels once and then reconfigure them instead of creating new widgets on every iteration.

To add to Bryan's answer you should also move the code for sleep to be outside of the tk instance and fix your indention.
Here is a corrected version of your code that will fix all the problems I can see.
Indention fix
Sleep moved outside of tk instance (not related to your main problem but is wrong just the same)
changed the Param_Window function to update labels instead of recreating them ever 0.8 seconds.
Code:
import tkinter as tk
import datetime
import time
time_elapse_flag = False
start_time = datetime.datetime.now().replace(microsecond=0)
def time_elapse():
global time_elapse_flag
global diff
if(not time_elapse_flag):
time_elapse_flag = True
time_elapse_count = tk.Label(window, text='', relief='sunken', bg='white', font='Helvetica 16 bold')
time_elapse_count.place(x=10, y=150, width=200, height=50)
start_time = datetime.datetime.now().replace(microsecond=0)
time_elapse_label = tk.Label(window, text='Recording data since:', font='Helvetica 12 bold')
time_elapse_label.place(x=10, y=120, width=200, height=30)
def update_time():
global start_time
global diff
current_time = datetime.datetime.now().replace(microsecond=0)
diff = current_time - start_time
time_elapse_count.config(text=diff)
time_elapse_label.after(1000, update_time)
update_time()
else:
time_elapse_flag = False
time_elapse_count = tk.Label(window, text='')
time_elapse_count.place(x=10, y=150, width=200, height=50)
time_elapse_label = tk.Label(window, text='')
time_elapse_label.place(x=10, y=120, width=200, height=30)
def Param_Window():
global time_elapse_flag
global diff
pw = tk.Toplevel()
pw.geometry('1200x550')
time_elapse_label = tk.Label(pw, text='Recording data since:', font='Helvetica 20 bold')
time_elapse_count = tk.Label(pw, text=diff, relief='sunken', bg='white', font='Helvetica 16 bold')
time_elapse_label.place(x=640, y=350, width=300, height=50)
time_elapse_count.place(x=640, y=400, width=200, height=50)
def update_values():
global diff
if(time_elapse_flag):
time_elapse_label.config(text='Recording data since:')
time_elapse_count.config(text=diff)
else:
time_elapse_label.config(text='')
time_elapse_count.config(text='')
pw.after(800, update_values)
update_values()
if __name__ == "__main__":
print('Automatically starting in')
for cd in range(5):
print(5-cd)
time.sleep(1)
window = tk.Tk()
window.geometry('550x250')
disp_param_button = tk.Button(window, text='Display Parameters', command=Param_Window, bg='sky blue', font='Helvetica 12 bold', width=20, state='normal', relief='raised')
disp_param_button.place(x=300, y=150)
time_elapse()
window.mainloop()

Related

How to create a mandatory window in tkinter

I am using python 3.7 and tkinter for making a GUI which saves all my important passwords which are saved in a file passwords.txt. In this I want to create a button in my main window which pops up another window with an entry box and a button(which will close the window) and till this window is not closed it will not let the user to interact with my old window.
Here's my codes:
from tkinter import *
from tkinter import ttk
root = Tk()
f = open("passwords.txt", "r")
list1 = []
for item in f.readlines():
item = item.replace("\n", "")
list1.append(item)
def secondwindow():
root2 = Tk()
root2.title("Secure Your Password")
root2.configure(bg="black")
root2.geometry('700x600')
frame_color = "#%02x%02x%02x" % (150,150,150)
# Create A Main frame
main_frame = Frame(root2, bg=frame_color)
main_frame.pack(fill=BOTH,expand=1)
# Create Frame for X Scrollbar
sec = Frame(main_frame, bg=frame_color)
sec.pack(fill=X,side=BOTTOM)
# Create A Canvas
my_canvas = Canvas(main_frame, bg="black")
my_canvas.pack(side=LEFT,fill=BOTH,expand=1)
# Add A Scrollbars to Canvas
x_scrollbar = ttk.Scrollbar(sec,orient=HORIZONTAL,command=my_canvas.xview)
x_scrollbar.pack(side=BOTTOM,fill=X)
y_scrollbar = ttk.Scrollbar(main_frame,orient=VERTICAL,command=my_canvas.yview)
y_scrollbar.pack(side=RIGHT,fill=Y)
# Configure the canvas
my_canvas.configure(xscrollcommand=x_scrollbar.set)
my_canvas.configure(yscrollcommand=y_scrollbar.set)
my_canvas.bind("<Configure>",lambda e: my_canvas.config(scrollregion= my_canvas.bbox(ALL)))
# Create Another Frame INSIDE the Canvas
second_frame = Frame(my_canvas, bg=frame_color)
# Add that New Frame a Window In The Canvas
my_canvas.create_window((0,0),window=second_frame, anchor="nw")
f = Frame(second_frame, borderwidth=2, relief=SUNKEN, bg=frame_color)
f.pack(side=TOP, fill=X)
Label(f, text="Secure Your Password", fg="white", bg=frame_color, font="Algerian 35 italic").pack()
f1 = Frame(second_frame, bg="black")
f1.pack(fill=BOTH, side=TOP, expand=1)
Label(f1, text="Application", fg="red", bg="black", font="Calibri 20 bold", pady=10, padx=60).grid(row=1, column=1)
Label(f1, text="Username", fg="red", bg="black", font="Calibri 20 bold", pady=10, padx=210).grid(row=1, column=2)
Label(f1, text="Password", fg="red", bg="black", font="Calibri 20 bold", pady=10, padx=198).grid(row=1, column=3, padx=140)
for i in range(len(list1)):
application = list1[i].split(";;;")[0]
username = list1[i].split(";;;")[1]
password = list1[i].split(";;;")[2]
Label(f1, text=application, fg="white", bg="black", font="Calibri 20 bold", pady=5).grid(row=i+2, column=1)
Label(f1, text=username, fg="white", bg="black", font="Calibri 20 bold", pady=5).grid(row=i+2, column=2)
Label(f1, text=password, fg="white", bg="black", font="Calibri 20 bold", pady=5).grid(row=i+2, column=3)
root2.mainloop()
def checkPassword(password, l):
if password == "a":
root.destroy()
secondwindow()
else:
l.config(text="Wrong Password")
def password_window():
root.geometry('450x270')
root.title("Secure Your Password")
root.minsize(450, 270)
root.maxsize(450, 270)
root.configure(bg="black")
Label(root, text="Secure Your Password", fg="white", bg="black", font="Algerian 24 italic").pack(side=TOP)
Label(root, text="Your Password", fg="white", bg="black", font="Clibri 15").pack(pady=10)
password = StringVar()
Entry(root, textvariable=password, bg="grey", fg="white", font="Calibri 15 bold").pack(pady=10)
Button(root, text="Login", bg="grey", fg="white", activebackground="grey", font="Calibri 10", command=lambda: checkPassword(password.get(), l)).pack(pady=8)
l = Label(root, fg="red", bg="black", font="Clibri 10 bold")
l.pack()
password_window()
root.mainloop()
And my passwords.txt:
StackOverflow;;;PomoGranade;;;PomoGranade_StackOverflow
GitHub;;;Pomogranade;;;PomoGranade_GitHub
I am new to python and tkinter. Thanks for help in advance :)
I do not recommend using * imports, though it may not be exactly wrong in this case.
Use the TopLevel widget instead of initialising another Tk window. See why using another Tk is not good.
Use .grab_set() (Look at #TheLizzard's link in the comment for a better example)
Look at this example -
import tkinter as tk
root = tk.Tk()
def f1():
top1 = tk.Toplevel(root)
b2 = tk.Button(top1,text='Close New Window',command=top1.destroy)
b2.pack()
top1.grab_set()
b1 = tk.Button(root,text='Create Mandatory Window',command=f1)
b1.pack()
root.mainloop()
If you run this code, you will see that the first window does not react to any mouse press etc... and also you cannot close the first window after opening the new window until the it is closed

Parameters passed to a function does not works as intended

When the "view" button is pressed, it should trigger the function solution(i) such that label should be displayed in the new window. The problem is that the window opens and the previous label is packed but the label which gets it's text from "i" does not gets packed, Is there any issue in passing the parameter.
Any help is appreciated.
root = Tk()
root.config(background = "#303939")
root.state('zoomed')
def pre():
with open("DoubtSolution.txt","r+") as f:
dousol = f.read()
dousol_lst = dousol.split("`")
k = 0
window = Tk()
window.config(background = "#303939")
window.state('zoomed')
predoubt = Label(window,
text="Previous Doubts",
fg="Cyan",
bg="#303939",
font="Helvetica 50 bold"
).grid(row=0, column=1)
def solution(text):
print(text)
window1 = Tk()
window1.config(background="#303939")
window1.state('zoomed')
sol = Label(window1,
text=text[:text.find("~")],
font=font.Font(size=20),
bg="#303939",
fg="Cyan")
sol.pack()
window1.mainloop()
for i in dousol_lst:
if i[-5:] == admno:
doubt = Label(window, text=i[i.find("]]")+2:i.find("}}}}")], font=font.Font(size=20), bg="#303939",
fg="Cyan")
doubt.grid(row=2+k, column=1, pady=10)
view = Button(
master=window,
text="View", font=font.Font(size=15, family="Helvetica"),
activebackground="White",
bg="Teal",
bd=0.8,
fg="White",
command = lambda k = k:solution(i)
)
view.grid(row=2+k, column=2, padx=20)
k=k+1
window.mainloop()
previous = Button(
master=root,
text="Previous Doubts", font="Helvetica 22 bold",
activebackground="White",
bg="Teal",
bd=0.8,
fg="White",
command = pre
).grid(row=4, column=3, padx=20)
root.mainloop()

How to change a frame in tkinter, after keeping it alive for a few seconds

I'm working on a color detector project with raspberry pi.
the flow is:
a welcome screen is displayed when the program starts, as soon as the color sensor detects a color it takes us to the next screen to enter a number.
As the color sensor used is TCS3200 and it continuously gives us value, many a time it happens to detect the color, even when there is no color and it takes to next screen.
What I want to do is, whenever a color is detected and it goes to the next screen, that screen should be active for 15 seconds, and should go back to the welcome screen again.
I tried using time.sleep, but whenever I issue that, the frame raise never happens, and after 10 seconds it shows me the welcome screen.
I'm putting my code here
import RPi.GPIO as GPIO
import time
from tkinter import *
import tkinter.font as tkFont
from PIL import Image, ImageTk
def raise_frame(frame):
frame.tkraise()
#Declare Global Variables
root = None
dfont = None
welcome = None
msg = None
value = None
number = ""
#GPIO pins
aux_vcc = 16
s2 = 5
s3 = 6
signal = 26
NUM_CYCLES = 10
#Fulscreen or windowed
fullscreen = False
def number_e():
global number
num = number.get()
number.set(num)
print(num)
num=""
number.set(num)
raise_frame(PageTwo)
def num_get(num):
current = e.get()
e.delete(0, END)
e.insert(0, str(current) + str(num))
def delt():
temp = e.get()[:-1]
e.delete(0, END)
e.insert(0, temp)
def clr():
e.delete(0, END)
def cancel():
raise_frame(welcome)
#toggle fullscreen
def toggle_fullscreen(event=None):
global root
global fullscreen
fullscreen = not fullscreen
root.attributes('-fullscreen', fullscreen)
resize()
#go into windowed mode
def end_fullscreen(event=None):
global root
global fullscreen
fullscreen = False
root.attributes('-fullscreen', False)
resize()
#resize font based on screen size
def resize(event=None):
global dfont
global welcome
new_size = -max(12, int((welcome.winfo_height() / 10)))
dfont.configure(size=new_size)
def setup():
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(signal, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(aux_vcc, GPIO.OUT)
GPIO.output(aux_vcc, GPIO.HIGH)
GPIO.setup(s2, GPIO.OUT)
GPIO.setup(s3, GPIO.OUT)
print("\n")
def endprogram():
GPIO.cleanup()
def loop():
temp = 1
global root
global value
global number
global num
GPIO.output(s2,GPIO.LOW)
GPIO.output(s3,GPIO.LOW)
time.sleep(0.3)
start = time.time()
for impulse_count in range(NUM_CYCLES):
GPIO.wait_for_edge(signal, GPIO.FALLING)
duration = time.time() - start #seconds to run for loop
red = NUM_CYCLES / duration #in Hz
print("red value - ",red)
GPIO.output(s2,GPIO.LOW)
GPIO.output(s3,GPIO.HIGH)
time.sleep(0.3)
start = time.time()
for impulse_count in range(NUM_CYCLES):
GPIO.wait_for_edge(signal, GPIO.FALLING)
duration = time.time() - start
blue = NUM_CYCLES / duration
print("blue value - ",blue)
GPIO.output(s2,GPIO.HIGH)
GPIO.output(s3,GPIO.HIGH)
time.sleep(0.3)
start = time.time()
for impulse_count in range(NUM_CYCLES):
GPIO.wait_for_edge(signal, GPIO.FALLING)
duration = time.time() - start
green = NUM_CYCLES / duration
print("green value - ",green)
time.sleep(0.5)
if ((red >= 3600 and red <= 4299) and (blue >= 3900 and blue <= 5850) and (green >= 5150 and green <= 6300)):
raise_frame(PageOne)
elif ((red >= 3600 and red <= 4999) and (blue >= 4199 and blue <= 4800) and (green >= 4500 and green <= 5950)):
raise_frame(PageOne)
elif ((red >= 4400 and red <= 4699) and (blue >= 4150 and blue <= 5000) and (green >= 4100 and green <= 4950)):
raise_frame(PageOne)
elif ((red >= 3600 and red <= 4399) and (blue >= 5000 and blue <= 5600) and (green >= 3900 and green <= 4499)):
raise_frame(PageOne)
else:
pass
root.after(500, loop)
#create the window
root = Tk()
root.title("Screen")
root.geometry('800x480')
welcome = Frame(root)
PageOne = Frame(root)
PageTwo = Frame(root)
for frame in (welcome, PageOne, PageTwo):
frame.grid(row=6, column=3, sticky='news')
value = DoubleVar()
msg = StringVar()
number = StringVar()
dfont = tkFont.Font(size=-6)
myfont = tkFont.Font(size=20)
mfont = tkFont.Font(size=12)
wel = Label(welcome, text="Welcome.", font=myfont)
wel.grid(row=1, column=1, padx=0, pady=0)
wel.place(x=50, y=320)
load = Image.open("banner.png")
load = load.resize((800,250), Image.BICUBIC)
render = ImageTk.PhotoImage(load)
img = Label(welcome, image=render)
img.image = render
img.place(x=0, y=0)
img.grid(row=0, column=0)
Label(PageOne, text="Enter your Mobile Number to get reward: ", font=myfont).grid(columnspan=3, row=0, column=0, padx=100, pady=50)
e = Entry(PageOne, textvariable=number, width=30, font=myfont)
e.grid(columnspan=3, row=1, column=0, padx=150, pady=15)
Button(PageOne, text='1', command=lambda:num_get(1), borderwidth=5, relief=RAISED, height=1, width=10, font=myfont).grid(row=2, column=0)
Button(PageOne, text='2', command=lambda:num_get(2), borderwidth=5, relief=RAISED, height=1, width=10, font=myfont).grid(row=2, column=1)
Button(PageOne, text='3', command=lambda:num_get(3), borderwidth=5, relief=RAISED, height=1, width=10, font=myfont).grid(row=2, column=2)
Button(PageOne, text='4', command=lambda:num_get(4), borderwidth=5, relief=RAISED, height=1, width=10, font=myfont).grid(row=3, column=0)
Button(PageOne, text='5', command=lambda:num_get(5), borderwidth=5, relief=RAISED, height=1, width=10, font=myfont).grid(row=3, column=1)
Button(PageOne, text='6', command=lambda:num_get(6), borderwidth=5, relief=RAISED, height=1, width=10, font=myfont).grid(row=3, column=2)
Button(PageOne, text='7', command=lambda:num_get(7), borderwidth=5, relief=RAISED, height=1, width=10, font=myfont).grid(row=4, column=0)
Button(PageOne, text='8', command=lambda:num_get(8), borderwidth=5, relief=RAISED, height=1, width=10, font=myfont).grid(row=4, column=1)
Button(PageOne, text='9', command=lambda:num_get(9), borderwidth=5, relief=RAISED, height=1, width=10, font=myfont).grid(row=4, column=2)
Button(PageOne, text='0', command=lambda:num_get(0), borderwidth=5, relief=RAISED, height=1, width=10, font=myfont).grid(row=5, column=1)
Button(PageOne, text='Delete', command=delt, borderwidth=5, relief=RAISED, height=1, width=10, font=myfont).grid(row=5, column=2)
Button(PageOne, text='Clear', command=clr, borderwidth=5, relief=RAISED, height=1, width=10, font=myfont).grid(row=5, column=0)
Button(PageOne, text='Enter', bg='#0052cc', fg='#ffffff', command=number_e, borderwidth=5, relief=RAISED, height=1, width=20, font=myfont).grid(row=6, column=0, columnspan=2)
Button(PageOne, text='Cancel', command=cancel, borderwidth=5, relief=RAISED, height=1, width=10, font=myfont).grid(row=6, column=2)
Label(PageTwo, text=" ", font=myfont).grid(row=0, column=1, padx=5, pady=5)
Label(PageTwo, text="Thank You", font=myfont).grid(row=1, column=1, padx=150, pady=200)
root.bind('<F11>', toggle_fullscreen)
root.bind('<Escape>', end_fullscreen)
root.bind('<Configure>', resize)
setup()
root.after(1000, loop)
raise_frame(welcome)
toggle_fullscreen()
root.mainloop()

How to add timer to each divide screen in tkinter

I created one window and split it into 3 different windows. I want to add a clock to each screen, that does not depend on the other clocks.
my code opens 2 window- one is the timer and the second one is the window that i split to 3 windows.
from tkinter import *
import Tkinter as tk
class split_screen(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.label = tk.Label(self, text="", width=10,fg="black", bg ="red", font="david 18 bold underline")
self.label.pack()
self.remaining = 0
self.countdown(1000)
self.configure(background="black")
def screen(self):
root = Tk()
root.geometry("650x700")
root.configure(bg='black')
root.title("test")
left = Frame(root, borderwidth=200, relief="solid")
right = Frame(root, borderwidth=20, relief="solid")
box3 = Frame(right, borderwidth=2, relief="solid")
box1 = Frame(left, borderwidth=2, relief="solid")
box2 = Frame(left, borderwidth=2, relief="solid")
label1 = Label(box3, text="winner's" + "\n\n\n" + "Player 1",fg= "black", bg = "red", font = "david 18 bold underline")
label2 = Label(box1, text="Computer 1",fg = "black", bg = "red", font= "david 18 bold underline")
label3 = Label(box2, text="Computer 2",fg = "black", bg = "red", font= "david 18 bold underline")
left.pack(side="left", expand=True, fill="both")
right.pack(side="right", expand=True, fill="both")
box3.pack(expand=True, fill="both", padx=10, pady=10)
box1.pack(expand=True, fill="both", padx=10, pady=10)
box2.pack(expand=True, fill="both", padx=10, pady=10)
label1.pack()
label2.pack()
label3.pack()
def countdown(self, remaining = None):
if remaining is not None:
self.remaining = remaining
if self.remaining <= 0:
self.label.configure(text="time's up!")
else:
self.label.configure(text="%d" % self.remaining)
self.remaining = self.remaining - 1
self.after(1000, self.countdown)
if __name__ == "__main__":
app = split_screen()
app.screen()
app.mainloop()
I did multiple changes :
As Bryan Oakley said, don't create multiple instances of Tk(). You also don't need to import tkinter twice.
import tkinter as tk
class split_screen(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.geometry("650x700")
self.configure(bg='black')
self.title("test")
left = tk.Frame(self, borderwidth=200, relief="solid")
right = tk.Frame(self, borderwidth=20, relief="solid")
box1 = tk.Frame(left, borderwidth=2, relief="solid")
box2 = tk.Frame(left, borderwidth=2, relief="solid")
box3 = tk.Frame(right, borderwidth=2, relief="solid")
label1 = tk.Label(box3, text="winner's" + "\n\n\n" + "Player 1",fg= "black", bg = "red", font = "david 18 bold underline")
label2 = tk.Label(box1, text="Computer 1",fg = "black", bg = "red", font= "david 18 bold underline")
label3 = tk.Label(box2, text="Computer 2",fg = "black", bg = "red", font= "david 18 bold underline")
clock1 = Clock(box1, 1000)
clock2 = Clock(box2, 2000)
clock3 = Clock(box3, 1300)
left.pack(side="left", expand=True, fill="both")
right.pack(side="right", expand=True, fill="both")
box3.pack(expand=True, fill="both", padx=10, pady=10)
box1.pack(expand=True, fill="both", padx=10, pady=10)
box2.pack(expand=True, fill="both", padx=10, pady=10)
label1.pack()
label2.pack()
label3.pack()
class Clock():
def __init__(self, frame, count):
self.frame = frame
self.label = tk.Label(frame, text="", width=10,fg="black", bg ="red", font="david 18 bold underline")
self.label.pack()
self.remaining = 0
self.countdown(count)
frame.configure(background="black")
def countdown(self, remaining = None):
if remaining is not None:
self.remaining = remaining
if self.remaining <= 0:
self.label.configure(text="time's up!")
else:
self.label.configure(text="%d" % self.remaining)
self.remaining = self.remaining - 1
self.frame.after(1000, self.countdown)
if __name__ == "__main__":
app = split_screen()
app.mainloop()
The simplest solution -- and arguably the best -- is to create a class that represents a single timer. You can then make as many instances as you want. This is precisely the sort of thing that classes are for: to encapsulate some behavior inside an object.
Since you're using a label to display the time, you can either have the timer class inherit from Label, or you can pass the label in when creating the timer.
Here's an example which inherits from tk.Label:
import tkinter as tk
class TimerLabel(tk.Label):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.after_id = None
self.remaining = None
def countdown(self, seconds):
if seconds <= 0:
self.configure(text="Time's up!")
else:
self.configure(text=f"{seconds}")
self.after_id = self.after(1000, self.countdown, seconds-1)
def stop(self):
if self.after_id:
self.configure(text="cancelled")
self.after_cancel(self.after_id)
Using the above class definition, you can create as many timer widgets as you want. Here's an example that creates three:
root = tk.Tk()
timer1 = TimerLabel(root, width=10)
timer2 = TimerLabel(root, width=10)
timer3 = TimerLabel(root, width=10)
timer1.pack(side="top", padx=20)
timer2.pack(side="top", padx=20)
timer3.pack(side="top", padx=20)
timer1.countdown(30)
timer2.countdown(20)
timer3.countdown(10)
root.mainloop()

how to show run time and loops per hour

I have a script which operates gpio's then loops until the loop is broken by a tkinter button, id like to see how many loops its doing per hour. im already able to see how many loops its done with a global variable but cannot work out nor find out how to make it show the current run time and how many loops per hour.. please help..
Here's the MCVE
import Tkinter as tk
root = tk.Tk()
from Tkinter import *
import time
import datetime
root.geometry("748x150")
root.title("Countdown Tester")
run_counter = 0
ts = datetime.datetime.now()
tf = datetime.datetime.now()
te = tf - ts
def _1():
label2["text"] = "1"
label2.config(bg='orange', fg='black')
root.update_idletasks()
time.sleep(1)
def _0():
label2["text"] = "0"
label2.config(bg='orange', fg='black')
root.update_idletasks()
time.sleep(1)
global run_counter
run_counter += 1
root.update_idletasks()
label6["text"] = run_counter
label7["text"] = run_counter# *********** Id like to have the run times per hour listed here
label8["text"] = te# *********** Id like to have the total run time listed here
root.update_idletasks()
canvas = Canvas(width=720, height=150, bg='black')
canvas.grid(rowspan=26, columnspan=20, sticky='W,E,N,S')
label2 = Label(root, width=11, font='-weight bold', background='black', foreground='white')
label2.grid(padx=5, pady=10, row=0, column=0, sticky='W,E,N,S')
label6 = Label(root, text=run_counter, width=11, font='-weight bold', background='white', foreground='black')
label6.grid(padx=5, pady=10, row=0, column=1, sticky='W,E,N,S')
label7 = Label(root, text=run_counter, width=11, font='-weight bold', background='white', foreground='black')
label7.grid(padx=5, pady=10, row=1, column=1, sticky='W,E,N,S')
label8 = Label(root, text=te, width=11, font='-weight bold', background='white', foreground='black')
label8.grid(padx=5, pady=10, row=1, column=0, sticky='W,E,N,S')
#root.resizable(width=FALSE, height=FALSE)
#timer()
while 1:
try:
_1()
_0()
root.update()
except KeyboardInterrupt:
break
root.mainloop()
for some reason this MCVE reports an error on _1() when quit

Categories