Tkinter code for a Timer is crashing the app (python) - python

I am creating a timer for my quiz app and I decided to try it out first in a separate program,
however when I run the following code and press the 'start timer' button the app simply stops responding
and I am forced to close it through the task manager
from tkinter import *
import time
root=Tk()
root.geometry('{}x{}'.format(300,200))
lb=Label(root,text='')
lb.pack()
def func(h,m,s):
lb.config(text=str(h)+':'+str(m)+':'+str(s))
time.sleep(1)
s+=1
func(h,m,s)
if s==59:
m=1
s=0
bt=Button(root,text='start timer',command=lambda:func(0,0,0))
bt.pack()
root.mainloop()

You need to use root.after instead of time.sleep. Here's another answer about making a timer. This is what it looks like applied to your code:
from tkinter import *
import time
root=Tk()
root.geometry('{}x{}'.format(300,200))
lb=Label(root,text='')
lb.pack()
def func(h,m,s):
lb.config(text=str(h)+':'+str(m)+':'+str(s))
s+=1
if s==59:
m=1
s=0
root.after(1000, lambda: func(h,m,s))
bt=Button(root,text='start timer',command=lambda:func(0,0,0))
bt.pack()
root.mainloop()

Related

Tkinter Python: How to continue running script without closing the messagebox popup

I have a doubt, how can I continue running the script without closing a messagebox from tkinter? Or show multiple messageboxes at the same time
An example:
from tkinter import messagebox as MB
aux = 0
def scrpt():
global aux
#the script stops here ⬇️
MB.showinfo(title="Simple Program", message="aux= "+str(aux))
#the code below isnt ejecuted if i didnt accept or close the above popup ⬆️
aux += 1
while True:
scrpt()
I want to know if there is a way to continue running the script without closing the tkinter messagebox popup.
I don't think that is possible with message box because I tried it with many ways but in vain, however …
you can do it by using Toplevel() class:
from tkinter import *
for i in range(0, 100):
a = Toplevel()
Label(a, text=str(i)).pack()
mainloop()
The problem is by closing one window all will close too, you can do this so it won't close together:
for i in range(0,100):
a= Tk()
mainloop()

Running a looping function and killing it when required Tkinter

I am using tkinter to build my application for testing something. I have a start button which starts a function myFunc() which is defined something like this:
def myFunc():
for i in range(20):
# do something which takes 0.5sec to execute
And I have another button which should stop this function when pressed, even if its not fully executed.
There are 2 problems here. First, the loop in myFunc() takes 0.5*20=10seconds to execute, which will freeze my application for that long. I can use threading to overcome this but the second problem is if I use someThread.join() as the command for the stop button, it will still wait until the function is executed. Also, although the GUI is not frozen while myFunc() is running, it hangs when I press stop button (because now its waiting until the function completes). So I need to kill the thread and stop executing that function AS SOON AS the stop button is pressed. This is a necessity.
I read everywhere on the internet that its not recommended to kill a thread. How do I do this elegantly? I dont want any problems or freezing. I just want the function to stop executing myFunc() the instant I press the stop button.
Full source code:
import tkinter as tk
from tkinter import ttk
import time
from threading import Thread
def myFunc():
for i in range(20):
time.sleep(0.5)
print('DONE')
def start():
someThread.start()
def stop():
someThread.join()
root.quit()
root = tk.Tk()
root.title('My app')
someThread = Thread(target=myFunc)
ttk.Button(root, text='Start', command=start).pack()
ttk.Button(root, text='Stop', command=stop).pack()
root.mainloop()
We would need to define a variable such as stopped which will tell the code if to stop the for loop or not. I have also added print('hi') and print('hi') to check if def stop() is triggered or not and if the for loop stopped or not.
Code
import tkinter as tk
from tkinter import ttk
import time
from threading import Thread
def myFunc():
global stopped
for i in range(20):
print('hi')
if stopped == True: #checks if stopped is True or False
break
time.sleep(.5)
print('DONE')
def start():
someThread.start()
def stop():
global stopped
stopped = True #makes stopped True which will stop the for loop/thread
print('triggered')
root = tk.Tk()
root.title('My app')
someThread = Thread(target=myFunc)
ttk.Button(root, text='Start', command=start).pack()
ttk.Button(root, text='Stop', command=stop).pack()
stopped = False #stopped is false so the for loop can run
root.mainloop()
hi and triggered
hi
hi
hi
triggered #pressed stop
hi
DONE

Python tKinter hanging

Why does in the code below button1 hang until the time.sleep(10) has completed.
I can only assume tKinter is waiting for the click event to finish before updating it's paint function.
I want on button1 click the state to change to DISABLED as in the code straight away, not when mainformbutton1press() has finished.
I have put time.sleep(10) to mimic rest of code functions - but the actual programme will be many minutes instead.
EDIT! - sleep is just there to show how tkinter hangs. My real programme has lots more code and no sleep function - and it takes a long time to process data with the hung GUI as mentioned. No more sleep suggestions please :)
import tkinter as tk
from tkinter import ttk
from tkinter.constants import DISABLED, NORMAL
import time
# ==================================================
class App:
def __init__(self, tk, my_w):
self.button1 = tk.Button(my_w, text="START", width=34, command = self.mainformbutton1press)
self.button1.grid(columnspan=3, row=6, column=1,padx=10,pady=20, ipadx=20, ipady=20)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def mainformbutton1press(self):
self.button1.config(text="PLEASE WAIT...")
self.button1['state'] = DISABLED
# DO REST OF PROCESSING
# TO MIMIC THIS:
time.sleep(10)
print("doing...")
# ==================================================
if __name__ == "__main__":
my_w = tk.Tk()
my_w.geometry("430x380")
my_w.resizable(False, False)
app = App(tk, my_w)
my_w.mainloop() # Keep the window open
Tk.mainloop is a sort of while loop. time.sleep() stops the loop for a particular period of time. That makes the window unresponsive. You might use .after function:
class App:
def __init__(self, tk, my_w):
self.my_w=my_w
....
def continue_next(self):
print("Doing")
....
def mainformbutton1press(self):
self.button1.config(text="PLEASE WAIT...")
self.button1['state'] = DISABLED
# DO REST OF PROCESSING
# TO MIMIC THIS:
self.my_w.after(10000,self.continue_next)
The only change you need to make to your code is to insert an update to your button.
The 10 second delay might need to be shortened (10 seconds is a long time to wait)
self.button1.config(text="PLEASE WAIT...")
self.button1['state'] = DISABLED
# INSERT UPDATE HERE
self.button1.update()
# DO REST OF PROCESSING
# TO MIMIC THIS:
time.sleep(1)
print("doing...")

tkinter progressbar does not work properly

I am facing problem with tkinter progressbar.
Below is my code.
I am trying to show a progressbar (indeterminate mode) which the program is running a function. But it only appear after the function is completed, not before. How can this happen? The progressbar was started before the function('test') is executed..
Many thanks
below is the code
from tkinter import *
from tkinter.ttk import *
import time
def progressbar(parent):
global pb
pb = Progressbar(parent, length=100, mode='indeterminate', maximum=100, value=50)
pb.pack(padx=2, pady=2, expand=YES, fill=X)
pb.start()
def test():
for i in range(10):
print(i)
time.sleep(0.5)
return i
root = Tk()
progressbar(root)
test()
root.mainloop()
The problem is that you call the test() function before the mainloop() as Sujay said.
You can solve this by creating some sort of a button, that when clicked on, the test() function will activate.

Create popups continuously in a loop without blocking the code

My program streams data and I want to create a popup displaying some text whenever a condition is met. I tried to create a simple tkinter window and ctypes window, but both seem to block my code, preventing it from continuing until the window has been closed. How can I create simple popup window functionality in for example a loop?
What I have so far is something of this structure.
import tkinter as tk
for i in range(11):
if i%5 == 0: # Any condition
popup = tk.Tk()
label = ttk.Label(popup, text='hi', font=('Verdana', 12))
label.pack(side='top', padx=10, pady=10)
popup.mainloop()
and
import ctypes
for i in range(11):
if i%5 == 0: # Any condition
popup = ctypes.windll.user32.MessageBoxW
popup(None, 'hi', 'title', 0)
However, in both cases the loop will not proceed until I close the popup.
Case 1 - Tkinter:
You are using mainloop() which is no different than a true while loop. You can make it run continuously by removing it.
import tkinter as tk
from tkinter import ttk
for i in range(11):
if i%5 == 0: # Any condition
popup = tk.Tk()
label = ttk.Label(popup, text='hi', font=('Verdana', 12))
label.pack(side='top', padx=10, pady=10)
Case 2 - Ctypes:
To make it run continuously, you will have to use threading.
import ctypes, threading
for i in range(11):
if i%5 == 0: # Any condition
popup = ctypes.windll.user32.MessageBoxW
threading.Thread(target = lambda :popup(None, 'hi', 'title', 0)).start()
Not too familiar with ctypes, but for tkinter the UI will always be blocking your main code during the mainloop.
You can somewhat bypass it if you just instantiate your Tk() without invoking mainloop and use the after function:
import tkinter as tk
from tkinter.messagebox import showinfo
root = tk.Tk()
root.withdraw()
for i in range(10):
if i in (5, 8):
root.after(ms=1, func=lambda x=i: showinfo('Message!', f'Stopped at {x}'))
# ... do something here
The root.after queues the message box to be displayed after 1 millisecond (ms=1).
A better way might be to create a modeless message/dialog box in ctypes but as mentioned, I'm not too familiar and a quick search didn't yield any simple solution.

Categories

Resources