I've been googeling all day, tried loads of different ways, but I can't get this code to work. I want a simple timer that run a function when you press "Start", and stops when you press "Stop".
Example:
When you press start, the function will print every second "Hello World", until you press stop.
My code with some comments so you can understand faster:
import sys
from Tkinter import *
from threading import Timer
# a boolean for telling the timer to stop
stop_timer = False
mgui = Tk()
def work(var):
# stop_timers sets to True or False with Start/Stop
stop_timer = var
# work_done will evaluate if timer should start or be canceled
def work_done(var2):
stop_timer = var2
# if stop was pressed t.start() will be ignored
if stop_timer == False:
t.start()
# if stop was pressed timer will stop
if stop_timer == True:
print "Stopped!"
t.cancel()
t = Timer(1, work, [False])
print "Something"
work_done(var)
mgui.geometry('450x450')
mgui.title('Test')
cmd1 = lambda: work(False)
btn = Button(mgui, text="Start", command =cmd1).place(x=50, y=50)
cmd2 = lambda: work(True)
btn2 = Button(mgui, text="Stop", command =cmd2).place(x=100, y=50)
mgui.mainloop()
As you can tell, I'm new to this shizzle!
Thanks, mates!
This is a generic timer, you can also implement one in tkinter using after:
import time
import Tkinter as tk
import threading
class MyTimer(threading.Thread):
def __init__(self, t):
super(MyTimer,self).__init__()
self.txt = t
self.running = True
def run(self):
while self.running:
self.txt['text'] = time.time()
mgui = tk.Tk()
mgui.title('Test')
txt = tk.Label(mgui, text="time")
txt.grid(row=0,columnspan=2)
timer = None
def cmd1():
global timer
timer = MyTimer(txt)
timer.start()
def cmd2():
global timer
if timer:
timer.running = False
timer= None
btn = tk.Button(mgui, text="Start", command =cmd1)
btn.grid(row=1,column=1)
btn2 = tk.Button(mgui, text="Stop", command =cmd2)
btn2.grid(row=1,column=2)
mgui.mainloop()
By editing some in xndrme's post, I finally got it to work. Thank you!
I'll post the code here for possible future googlers.
import sys
from Tkinter import *
from threading import Timer
mgui = Tk()
def cmd2():
global t
if t:
t.cancel()
def looper():
global t
t = Timer(1, looper)
t.start()
print "Hello World!"
mgui.geometry('450x450')
mgui.title('Test')
btn = Button(mgui, text="Start", command =looper).place(x=50, y=50)
btn2 = Button(mgui, text="Stop", command =cmd2).place(x=100, y=50)
mgui.mainloop()
Related
In the code below, when I press Button 1, the application does not respond and I cannot use other Button 2.
Threading module was required for this.
I have created Threads for Threadingle main() and process(), but when the program opens and I press Button 1, the application does not respond and I cannot press Button 2.
What's the problem?
from tkinter import *
import threading
def process():
while True:
print("Hello World")
processThread = threading.Thread(target=process)
def main():
mainWindow = Tk()
mainWindow.resizable(FALSE, FALSE)
mainWindow.title("Text")
mainWindow.geometry("500x250")
recButton=Button(mainWindow)
recButton.config(text="Button 1", font=("Arial", "13"), bg="red",fg="white", width="15", command=processThread.run)
recButton.place(x=15,y=10)
stopButton=Button(mainWindow)
stopButton.config(text="Button 2", font=("Calibri", "13"), bg="orange",fg="white", width="15", command="")
stopButton.place(x=15,y=55)
textBox = Text(mainWindow, height="14", width="37")
textBox.place(x=180, y=10)
mainWindow.mainloop()
mainThread = threading.Thread(target=main)
mainThread.start()
use processThread.start to start the thread instead of processThread.run infact processThread.run will just call the process method and which will not return control to your event loop hence application goes wild
command=processThread.start
from tkinter import *
import threading
def process():
while True:
print("Hello World")
processThread = threading.Thread(target=process)
def main():
mainWindow = Tk()
mainWindow.resizable(FALSE, FALSE)
mainWindow.title("Text")
mainWindow.geometry("500x250")
recButton=Button(mainWindow)
recButton.config(text="Button 1", font=("Arial", "13"), bg="red",fg="white", width="15", command=processThread.start)
recButton.place(x=15,y=10)
stopButton=Button(mainWindow)
stopButton.config(text="Button 2", font=("Calibri", "13"), bg="orange",fg="white", width="15", command="")
stopButton.place(x=15,y=55)
textBox = Text(mainWindow, height="14", width="37")
textBox.place(x=180, y=10)
mainWindow.mainloop()
mainThread = threading.Thread(target=main)
mainThread.start()
Modified the above code for start and stop button functionality. Here we are using flag when this is true thread will be running if flag if false the thread will stop. Remember we can't start a stopped thread because of this we have to create new thread.
import time
from tkinter import *
import threading
flag = True
def printHello():
global flag
while flag:
print("Hello World")
time.sleep(1)
"""
starts the thread
"""
def startProcess():
global flag
flag = True
processThread = threading.Thread(target=printHello)
processThread.start()
"""
stops the thread
"""
def stopProcess():
global flag
flag = False
def main():
mainWindow = Tk()
mainWindow.resizable(FALSE, FALSE)
mainWindow.title("Text")
mainWindow.geometry("500x250")
recButton = Button(mainWindow)
recButton.config(text="Button 1", font=("Arial", "13"), bg="red", fg="white", width="15",
command=startProcess)
recButton.place(x=15, y=10)
stopButton = Button(mainWindow)
stopButton.config(text="Button 2", font=("Calibri", "13"), bg="orange", fg="white", width="15", command=stopProcess)
stopButton.place(x=15, y=55)
textBox = Text(mainWindow, height="14", width="37")
textBox.place(x=180, y=10)
mainWindow.mainloop()
mainThread = threading.Thread(target=main)
mainThread.start()
So I am making a stopwatch program with tkinter.
At the moment, I am in the process of implementing a stop button for the stopwatch.
I can start the stopwatch and it will update the Label fine but my end button isn't working and I can't seem to figure out why.
Here's my code so far:
from tkinter import *
from time import sleep
import threading
root = Tk()
root.title("Clock")
class Buttons:
def __init__(self, master):
self.menu = Menu(master)
master.config(menu=self.menu)
self.menu.add_command(label="Stopwatch")
self.menu.add_command(label="Timer")
self.menu.add_command(label="Quit", command=quit)
self.stopwatchStart = Button(master, text="Start", command=self.test_start)
self.stopwatchStart.config(height=2, width=5)
self.stopwatchStart.grid(row=0)
self.stopwatchEnd = Button(master, text="End", command=self.stopwatch_End)
self.stopwatchEnd.config(height=2, width=5)
self.stopwatchEnd.grid(row=1)
self.labelSeconds = Label(master, text="Seconds:")
self.labelSeconds.grid(row=0, column=1)
self.stopwatchSeconds = Label(master, text="0")
self.stopwatchSeconds.grid(row=0, column=2)
def test_start(self):
print("Starting thread")
t = threading.Thread(target=self.stopwatch_Start)
t.setDaemon(True)
t.start()
def stopwatch_Start(self, test=False):
print("Stopwatch started")
stopwatch_seconds = "0"
stopwatchBreak = test
print(f"stopwatchBreak == {stopwatchBreak}")
while not stopwatchBreak:
print(stopwatch_seconds)
stopwatch_seconds = int(stopwatch_seconds)
stopwatch_seconds += 1
self.stopwatchSeconds.config(text=stopwatch_seconds)
root.update()
sleep(1)
print("Stopping stopwatch")
return
def stopwatch_End(self):
Buttons.stopwatch_Start(self, True)
print("Attempting to end")
b = Buttons(root)
root.mainloop()
I am using threading to run the stop watch and the window from tkinter at the same time by the way.
Also I have put several print() across the functions to see what is succeeding and what isn't. I think the problem may have something to do with the thread in test_start(). The loop that won't end when I click the end button is in stopwatch_Start(). It just keeps counting up.
I have received no error messages
Does anyone have any suggestions/alternatives to stopping the stopwatch?
look at this code
def stopwatch_End(self):
Buttons.stopwatch_Start(self, True)
print("Attempting to end")
this is your problem =>
Buttons.stopwatch_Start(self, True)
correct this with:
self.stopwatch_Start(True)
here you are call "stopwatch" on the class Buttons, this is an unbound function!!!
"Buttons.stopwatch" is different than "self.stopwatch" the later is bound to the instance.
from tkinter import *
from time import sleep
from threading import Thread
from threading import Event
root = Tk()
root.title("Clock")
class Buttons:
def __init__(self, master):
self.evt = Event()
self.menu = Menu(master)
master.config(menu=self.menu)
self.menu.add_command(label="Stopwatch")
self.menu.add_command(label="Timer")
self.menu.add_command(label="Quit", command=quit)
self.stopwatchStart = Button(master, text="Start", command=self.test_start)
self.stopwatchStart.config(height=2, width=5)
self.stopwatchStart.grid(row=0)
self.stopwatchEnd = Button(master, text="End", command=self.stopwatch_End)
self.stopwatchEnd.config(height=2, width=5)
self.stopwatchEnd.grid(row=1)
self.labelSeconds = Label(master, text="Seconds:")
self.labelSeconds.grid(row=0, column=1)
self.stopwatchSeconds = Label(master, text="0")
self.stopwatchSeconds.grid(row=0, column=2)
def test_start(self):
print("Starting thread")
t = Thread(target=self.stopwatch_Start)
t.setDaemon(True)
t.start()
def stopwatch_Start(self, test=False):
print("Stopwatch started")
stopwatch_seconds = "0"
stopwatchBreak = test
print(f"stopwatchBreak == {stopwatchBreak}")
# self.evt.is_set() will force the loop to check its actually state if it True or not
while not self.evt.is_set():
print(stopwatch_seconds)
stopwatch_seconds = int(stopwatch_seconds)
stopwatch_seconds += 1
self.stopwatchSeconds.config(text=stopwatch_seconds)
root.update()
sleep(1)
print("Stopping stopwatch")
return
def stopwatch_End(self):
#we set self.evt to True so that the while loop will be broken
self.evt.set()
print("Attempting to end")
b = Buttons(root)
root.mainloop()
I have a problem with stopping the program. When I click exit button, the mainloop stops but program is still running. I am new to it and I have no idea what to do. I know the issue is the thread is still running and I don't know how to stop it.
This is my code:
from tkinter import *
import simulation as s
import graph as g
import numpy as np
from tkinter import filedialog
import threading
def main():
root = Tk()
root.title("Simulator")
switch = True
def get_filename():
return filedialog.askopenfilename(parent=root)
def play():
def run():
while switch:
s.simulation(s.particle, np.inf, s.initial_time_step, get_filename())
thread = threading.Thread(target=run)
thread.start()
def start_simulation():
global switch
switch = True
v.set('Simulation is running!')
play()
def stop_simulation():
global switch
v.set('Simulation is stopped!')
switch = False
def draw_graphs():
g.create_graphs()
start = Button(root, text='Start simulation', command=start_simulation, width=50)
start.pack()
finish = Button(root, text='Stop simulation', command=stop_simulation, width=50)
finish.pack()
graphs = Button(root, text='Graphs', command=draw_graphs, width=50)
graphs.pack()
exit_button = Button(root, text='Exit', command=root.destroy, width=50)
exit_button.pack()
v = StringVar()
statement = Label(root, textvariable=v)
statement.pack()
root.mainloop()
if __name__ == '__main__':
main()
Create a function for the exit button which also stops your thread
def exit():
switch = False
root.destroy()
And later:
exit_button = Button(root, text='Exit', command=exit, width=50)
You can also call the same method whenever the window is closed with the top right X button. Just bind your method to the event:
root.protocol("WM_DELETE_WINDOW", exit)
Ps: You dont need to use global here, because your nested functions have access to the outer functions variables
I am having a problem where my Tkinter UI becomes completley stuck and non-interactive while a for loop is running. My example code print "Looping" while it is in the loop and there is a "Cancel" button on the UI which is supposed to stop the loop, but since I am unable to click the "Cancel" button the loop can not be stopped. So my question is how can I make my tkinter UI usable while a loop is running. Here is the example code:
from tkinter import*
import time
root = Tk()
i=10
flag = False
def loop():
flag = True
for i in range(100):
if flag == True:
time.sleep(0.5)
print("Looping")
def canc():
flag = False
btn = Button(root, text="Start Loop", command=loop).pack()
cncl = Button(root, text="Cancel", command=canc).pack()
root.mainloop()
I have tried creating a new thread for the loop function but this does not work.
Updated code, the UI is responsive, but nothing happens when cancel is pressed:
from tkinter import*
import threading
import time
root = Tk()
i=10
flag = False
def loop():
flag = True
for i in range(10):
if flag == True:
time.sleep(0.5)
print("Looping")
def run():
threading.Thread(target=loop).start()
def canc():
flag = False
btn = Button(root, text="Start Loop", command=run).pack()
cncl = Button(root, text="Cancel", command=canc).pack()
root.mainloop()
'flag' isnt a global variable so when it is set to False in canc(), the value of the 'flag' local variable in loop() isnt changed and the loop therefore isnt stopped
also root.update() needs to be used to update the GUI
Remedial actions:
from tkinter import*
import threading
import time
root = Tk()
def loop():
global flag
flag = True
for i in range(10):
if flag == True:
root.update()
time.sleep(0.5)
print("Looping")
def canc():
global flag
flag = False
btn = Button(root, text="Start Loop", command=loop).pack()
cncl = Button(root, text="Cancel", command=canc).pack()
root.mainloop()
I found a way around that problem:
I started my time-consuming job inside a thread and I checked if my thread is still running in a while loop, and inside that, I did update my Tkinter root.
here is my code:
def start_axis(input):
print(input)
time.sleep(5)
def axis():
t = threading.Thread(target=start_axis, args=("x"))
t.start()
while t.is_alive():
try:
root.update()
except:
pass
the args part is important so the thread doesn't call the function immediately
I was writing a program with a start page, and two programs that are called from that start page. Both of the subprograms work by themselves. However, when I put them into my start page, the stopwatch timing label doesn't show up. If you are wondering, I put them into my program by doing:
import program
program.function()
Here is my start page program:
from Tkinter import *
class start_page:
def __init__(self,master):
self.master = master
self.frame = Frame(self.master)
self.countdown = Button(master, text = "Timer", command = self.c).pack()
self.stopwatch_butt = Button(master,text="Stopwatch",command=self.g).pack()
def g(self):
import stopwatch
stopwatch.f()
def c(self):
import timer_prog
timer_prog.timer()
self.master.after_cancel(timer_prog)
def main():
root = Tk()
s = start_page(root)
root.title("Timer Suite: Brian Ton")
root.mainloop()
main()
If I run this program, the timer program works fine, but the stopwatch doesn't show its label, only its buttons. I tried to clear all Tk after functions, and that didn't work, and I also tried to run the stopwatch program first, to no avail.
Here is my stopwatch program:
from Tkinter import *
import datetime
def s():
start.config(state='disabled')
stop.config(state="normal")
reset.config(state='disabled')
Start()
def Start():
if reset['state'] == 'disabled' and stop['state'] == 'normal':
hidden.set(str(int(hidden.get())+1))
root.update()
root.after(1000,Start)
curr = hidden.get()
g.set(str(datetime.timedelta(seconds=int(curr))))
print g.get()
else:
return None
def Stop():
start.config(state='disabled')
stop.config(state='disabled')
reset.config(state="normal")
def Reset():
start.config(state="normal")
stop.config(state="disabled")
reset.config(state='disabled')
hidden.set('0')
g.set(str(datetime.timedelta(seconds=0)))
def f():
global root,frame,master,hidden,g,timelabel,start,stop,reset
root = Tk()
frame = Frame(root)
master = root
hidden = StringVar()
g = StringVar()
hidden.set('0')
timelabel = Label(master,textvariable=g)
g.set(str(datetime.timedelta(seconds=int(0))))
timelabel.grid(row=1,column=2)
start = Button(master,text="Start",command = s,state="normal")
stop = Button(master,text="Stop",command = Stop,state = "disabled")
reset = Button(master,text="Reset",command = Reset,state = "disabled")
start.grid(row=2,column=1)
stop.grid(row=2,column=2)
reset.grid(row=2,column=3)
root.update()
root.mainloop()
And here is my timer program:
from Tkinter import *
import datetime
def get_seconds(h,m,s):
hr_sec = h * 3600
m_sec = m * 60
return hr_sec+m_sec+s
def timerstartstop():
hours = hour_entry.get()
minutes = minute_entry.get()
sec = second_entry.get()
if hours == "":
hours = 0
hour_entry.insert(0,"0")
if minutes == "":
minutes = 0
minute_entry.insert(0,"0")
if sec == "":
sec = 0
second_entry.insert(0,"0")
c = get_seconds(int(hours), int(minutes), int(sec))
global s
s = StringVar(master)
s.set(c)
if startstop['text'] == 'Stop':
global curr
curr = shown
s.set(-1)
if startstop['text'] == 'Reset':
startstop.config(text="Start")
s.set(c)
root.update()
shown.set(str(datetime.timedelta(seconds=int(s.get()))))
return None
countdown()
import winsound
def countdown():
startstop.config(text="Stop")
global shown
good = True
shown = StringVar(master)
shown.set(str(datetime.timedelta(seconds=int(s.get()))))
L = Label(master,textvariable=shown).grid(row=1,column=2)
if int(s.get()) == 0:
startstop.config(text="Reset")
while startstop['text'] != "Start":
root.update()
winsound.Beep(500,500)
elif int(s.get()) < 0:
good = False
shown.set(curr.get())
startstop.config(text="Reset")
else:
if good:
s.set(str(int(s.get())-1))
root.after(1000,countdown)
def ex():
root.after_cancel(countdown)
root.destroy()
def timer():
global root
global master
global frame
root = Tk()
master = root
frame = Frame(master)
global hour_entry
hour_entry = Entry(master,width=3)
hour_entry.grid(row=0,column=0)
colon_l = Label(master,text=':').grid(row=0,column=1)
global minute_entry
minute_entry = Entry(master,width=2)
minute_entry.grid(row=0,column=2)
colon_l2 = Label(master,text=':').grid(row=0,column=3)
global second_entry
second_entry = Entry(master,width=2)
second_entry.grid(row=0,column=4)
global startstop
startstop = Button(master,text="Start",command=timerstartstop)
e = Button(master,text="Exit",command=ex).grid(row=1,column=3)
startstop.grid(row=0,column=5)
root.mainloop()
In addition, I tried to run these two programs from a different starting menu that used the console, which worked.
The console program is:
import timer_prog
timer_prog.timer()
raw_input('next')
import stopwatch
stopwatch.f()
Attached are some screenshots of what the stopwatch program should look like vs what it does look like when called from the starting program.
Note: I can tell the program is running from the starting page, as it prints the current time each second. Also, I attached some screenshots
Stopwatch Program Run Directly
Stopwatch Program Run From The Start Page
Tkinter program should use only one Tk() - to create main window - and one mainloop() - to control all windows and widgets. If you use two Tk() and two mainloop() then it has problem - for example get()/set() may not work.
Subwindows should use Toplevel() instead of Tk().
Function which starts program (ie. run()) could run with parameter window (def run(window)) and then you can execute it as standalone program with
root = Tk()
run(root)
root.mainloop()
or after importing
run(Toplevel())
(without maniloop())
You can use if __name__ == "__main__" to recognize if program starts as standalone.
Example
main.py
from Tkinter import *
class StartPage:
def __init__(self, master):
self.master = master
master.title("Timer Suite: Brian Ton")
Button(master, text="Timer", command=self.run_timer).pack()
Button(master, text="Stopwatch", command=self.run_stopwatch).pack()
def run_stopwatch(self):
import stopwatch
window = Toplevel()
stopwatch.run(window)
def run_timer(self):
import timer_prog
window = Toplevel()
timer_prog.timer(window)
self.master.after_cancel(timer_prog)
def main():
root = Tk()
StartPage(root)
root.mainloop()
main()
stopwatch.py
from Tkinter import *
import datetime
def pre_start():
start_button.config(state='disabled')
stop_button.config(state='normal')
reset_button.config(state='disabled')
start()
def start():
global current_time
# stop_button['state'] can be 'normal' or 'active' so better use ` != 'disabled'`
if reset_button['state'] == 'disabled' and stop_button['state'] != 'disabled':
current_time += 1
time_var.set(str(datetime.timedelta(seconds=current_time)))
print(time_var.get())
master.after(1000, start)
def stop():
start_button.config(state='disabled')
stop_button.config(state='disabled')
reset_button.config(state='normal')
def reset():
global current_time
start_button.config(state='normal')
stop_button.config(state='disabled')
reset_button.config(state='disabled')
current_time = 0
time_var.set(str(datetime.timedelta(seconds=0)))
def run(window):
global master
global current_time, time_var
global start_button, stop_button, reset_button
master = window
current_time = 0
time_var = StringVar()
time_var.set(str(datetime.timedelta(seconds=0)))
time_label = Label(window, textvariable=time_var)
time_label.grid(row=1, column=2)
start_button = Button(master, text='Start', command=pre_start, state='normal')
stop_button = Button(master, text='Stop', command=stop, state='disabled')
reset_button = Button(master, text='Reset', command=reset, state='disabled')
start_button.grid(row=2, column=1)
stop_button.grid(row=2, column=2)
reset_button.grid(row=2, column=3)
if __name__ == '__main__':
# it runs only in standalone program
root = Tk()
run(root)
root.mainloop()