tkinter unkown error with window updating - python

i wrote a simple program in tkinter to remind me for medicines every fixed interval of time, and when the alarm rings a snooze button should appear which should set the timer to 10 minutes and after that alarm should ring again and if stop is pressed instead of snooze this time it should reset the timer to initially given fixed interval of time.
i don't understand why the snooze button never appears after alarm plays
here's the code :-
import tkinter as tk
import sounddevice as sd
import soundfile as sf
import time
stop = False
def start_alarm():
global interval
interval = int(entry.get()) # get the interval from the user
time_left.config(text=str(interval) + " seconds left")
countdown()
def stop_alarm():
global stop
stop = True
sd.stop()
start_alarm()
def snooze_alarm():
global stop
stop = True
sd.stop()
interval += 600
time_left.config(text=str(interval) + " seconds left")
countdown()
def countdown():
global interval
global stop
if interval > 0 and not stop:
interval -= 1
time_left.config(text=str(interval) + " seconds left")
time_left.after(1000, countdown)
elif not stop:
play_alarm()
def play_alarm():
data, fs = sf.read('alarm.wav', dtype='float32')
sd.play(data, fs)
status = sd.wait()
create_snooze_button()
def create_snooze_button():
global snooze_button
snooze_button = tk.Button(root, text="Snooze", command=snooze_alarm)
snooze_button.pack()
root = tk.Tk()
root.title("Medicine Reminder")
label = tk.Label(root, text="Enter time interval (in seconds):")
label.pack()
entry = tk.Entry(root)
entry.pack()
start_button = tk.Button(root, text="Start", command=start_alarm)
start_button.pack()
stop_button = tk.Button(root, text="Stop", command=stop_alarm)
stop_button.pack()
time_left = tk.Label(root, text="")
time_left.pack()
root.mainloop()
any help is highly appreciated

Related

why is the display not updating

i want to do a countdown but the changes are not showing until the final one. Example:
def countdown():
# setup of window and other labels etc
timer = calculated time # just placeholder
countdown_time = Message(countdownWindow, text=timer, font=("Courier",16,"bold"), width=100)
time.sleep(1)
while timer != "00:00:00":
timer = calculate time # this time will be a updated current time
root.after(1000, countdown_time.configure(text=timer)
root.after(1000, countdown)
the problem here is that it only opens the window once the final change has been complete, but I want it to update the label every time it is changed
according to your example:
from tkinter import Tk, Button, Label
import time
def countdown():
time_count = 0
time.sleep(1)
while time_count != 10:
time_count += 1
root.after(1000, lbl.configure(text=str(time_count)))
root.update()
root.after(1000, countdown) # invokes an endless loop
root = Tk()
btn = Button(root, text="countdown", command=countdown)
btn.pack()
lbl = Label(root, text="")
lbl.pack()
root.mainloop()

Call a fonction from message box Tkinter

I want to call the fonction (submit) after clicking on the message box. With this code I have to reclick on button everytime after the messagebox but would appreciate if the timer launch automatically after clicking on the message box.
If anyone have a clue I will appreciate.
The following code :
import time
from tkinter import *
root = Tk()
root.resizable(width=False, height=False)
root.geometry("300x250")
root['background']='#39E5F9'
root.title("Time To Drink Water")
minute = StringVar()
second = StringVar()
minute.set("45")
second.set("00")
minuteEntry = Entry(root, width=3, font=("Arial", 35, ""),
textvariable=minute,justify='center')
minuteEntry.place(x=50, y=60)
secondEntry = Entry(root, width=3, font=("Arial", 35, ""),
textvariable=second,justify='center')
secondEntry.place(x=170, y=60)
def submit():
# stored in here : 2700 = 45 mins
temp = 2700
while temp > -1:
# divmod(firstvalue = temp//60, secondvalue = temp%60)
mins, secs = divmod(temp, 60)
if mins > 60:
hours, mins = divmod(mins, 60)
minute.set("{0:2d}".format(mins))
second.set("{0:2d}".format(secs))
root.update()
time.sleep(1)
if (temp == 0):
messagebox.showinfo("Time Countdown", "Time To Drink !")
temp -= 1
def go():
btn = Button(root, text='Goodbye dehydration!', bd='6',
command=submit)
btn.place(x=90, y=160)
go()
root.mainloop()
messagebox is waiting for your click so you can run submit() after messagebox and it will run it after clicking in messagebox
But you shouldn't use while and sleep because it may freeze GUI (in any framework, and in any language). You can use root.after(1000, function) to run function again after 1000ms (1s) and it will work as sleep and while together.
import time
import tkinter as tk # PEP8: `import *` is not preferred
from tkinter import messagebox
# --- functions --- # PEP8: all functions before main code
def submit():
update_counter(2700)
def update_counter(temp):
if temp > -1: # `if` instead of `while` because `after` will work as loop
# divmod(firstvalue = temp//60, secondvalue = temp%60)
mins, secs = divmod(temp, 60)
if mins > 60:
hours, mins = divmod(mins, 60)
minute.set("{:02d}".format(mins)) # use `:02` to get `09` instead of ` 9` (with space)
second.set("{:02d}".format(secs))
temp -= 1
root.after(1000, update_counter, temp) # run again after 1000ms
else:
messagebox.showinfo("Time Countdown", "Time To Drink !")
root.after(0, update_counter, 2700) # run again after 0ms
#update_counter(2700) # run again
# --- main --- # PEP8: `lower_case_names` for variables
running = False
temp = 2700
root = tk.Tk()
minute = tk.StringVar(root)
second = tk.StringVar(root)
minute.set("45")
second.set("00")
minute_entry = tk.Entry(root, width=3, textvariable=minute, font=("Arial", 35, ""), justify='center')
minute_entry.grid(row=0, column=0)
second_entry = tk.Entry(root, width=3, textvariable=second, font=("Arial", 35, ""), justify='center')
second_entry.grid(row=0, column=1)
btn = tk.Button(root, text='Goodbye dehydration!', command=submit)
btn.grid(row=1, column=0, columnspan=2)
root.mainloop()
PEP 8 -- Style Guide for Python Code
There is other problem. You can click button two times and it will run two update_counter() at the same time. It may need to disable button, or you would have to use boolean variable - ie. running = False - to control if it has to run update_counter() or not.

Can't stop timer in python (after and after_cancel)

I am trying to create a timer in a tkinter GUI that initiates on a start button, and stops on a stop button. I am using .after to loop the timer, but I can't correctly integrate .after_cancel. Any advice would be greatly appreciated.
#import python modules
import time
import math
from tkinter import *
#Create a root window to work in
root = Tk()
root.title("CNT Synthesis Controller") #title of file
#Create global variables to be stored
global var_status
global current_time
global start_time
current_time = 0
#Create Labels
myLabel_Status_T = Label(root, text="Total time elapsed (hrs:min:sec):")
myLabel_Timer = Label(root, text="00:00:00")
#Locate labels
myLabel_Status_T.grid(row=0, column=0)
myLabel_Timer.grid(row=1, column=0)
#Start button function
def Click_Start():
#disable Start Button to prevent re-start
myButton_Start.config(state=DISABLED)
#initiate time = 0 and start timer
start_time = time.time()
global Timer_continue
Timer_continue = True
#Timer function
def Timer():
#determine the amount of time passed since start_time measured
current_time = int(time.time()-start_time)
hour = math.floor(current_time/3600)
minute = math.floor((current_time/60)-(hour*60))
second = math.floor(current_time-(hour*3600)-(minute*60))
#shows time as 00:00:00, i.e. adding in the zeroes where needed
if hour<10:
hour=str("0"+str(hour))
else:
hour=str(hour)
if minute<10:
minute=str("0"+str(minute))
else:
minute=str(minute)
if second<10:
second=str("0"+str(second))
else:
second=str(second)
#print the time to the label myLabel_Timer
myLabel_Timer.config(text= hour + ":" + minute + ":" + second)
#after 1000 ms repeat the Timer function
#while (myButton_Stop['state'] != tk.DISABLED):
#Timer_Object=myLabel_Timer.after(1000,Timer)
if Timer_continue == True:
root.after(1000,Timer)
if Timer_continue == False:
root.after_cancel(Timer)
Timer()
#Stop button function
def Click_Stop():
Timer_continue = False
#disable Stop Button to prevent re-use until program is reset
myButton_Stop.config(state=DISABLED)
#Create Buttons
myButton_Start = Button(root,text="Start CNT Synthesis", padx=40, pady=20, fg="white", bg="green", command=Click_Start)
myButton_Stop = Button(root,text="Stop CNT Synthesis", padx=40, pady=20, fg="white", bg="red", command=Click_Stop)
#Locate buttons
myButton_Start.grid(row=2, column=0)
myButton_Stop.grid(row=2, column=1)
root.mainloop()
####---------------------------------------------------------------------Required added text to make the question 'long enough'--------------------------------------------------------------------------------------------##########
You need to save the ID returned by .after() and use it in .after_cancel() to stop the timer inside Click_Stop() function:
#import python modules
import time
from tkinter import *
#Create a root window to work in
root = Tk()
root.title("CNT Synthesis Controller") #title of file
#Create Labels
myLabel_Status_T = Label(root, text="Total time elapsed (hrs:min:sec):")
myLabel_Timer = Label(root, text="00:00:00")
#Locate labels
myLabel_Status_T.grid(row=0, column=0)
myLabel_Timer.grid(row=1, column=0)
Timer_id = None # used to store the ID returned by .after()
#Start button function
def Click_Start():
#disable Start Button to prevent re-start
myButton_Start.config(state=DISABLED)
#enable Stop Button
myButton_Stop.config(state=NORMAL)
#save the start timer
start_time = time.time()
#Timer function
def Timer():
global Timer_id
#determine the amount of time passed since start_time measured
elapsed = int(time.time()-start_time)
minutes, seconds = divmod(elapsed, 60)
hours, minutes = divmod(minutes, 60)
myLabel_Timer.config(text=f"{hours:02}:{minutes:02}:{seconds:02}")
#after 1000 ms repeat the Timer function
Timer_id = root.after(1000,Timer) # save the after ID
Timer()
#Stop button function
def Click_Stop():
if Timer_id:
root.after_cancel(Timer_id) # cancel the scheduled task
#disable Stop Button to prevent re-use until program is reset
myButton_Stop.config(state=DISABLED)
#enable Start Button
myButton_Start.config(state=NORMAL)
#Create Buttons
myButton_Start = Button(root,text="Start CNT Synthesis", padx=40, pady=20,
fg="white", bg="green", command=Click_Start)
myButton_Stop = Button(root,text="Stop CNT Synthesis", padx=40, pady=20,
fg="white", bg="red", command=Click_Stop, state=DISABLED) # disable stop button initially
#Locate buttons
myButton_Start.grid(row=2, column=0)
myButton_Stop.grid(row=2, column=1)
root.mainloop()
Miss the global statement:
def Click_Stop():
global Timer_continue
Timer_continue = False

How to fix timer increasing countdown speed with each button press

I already read a thread that helped my problem with turning the button[state] to 'normal' and 'disabled'.
I would really like to know and learn if there is a conditional/boolean way to do this.
How, or can you start a timer with a button and not have the timer speed up if a user clicks the start button again?
I tried several times to implement a conditional, checking if the button had been pressed, if the time was less than the original variable amount, etc.
But each time the timer would countdown faster and faster with each click. I feel like there is an issue on my part in understanding the fundamentals of how conditionals/booleans work.
from tkinter import *
root = Tk()
hold_time = 120
timer = False
def start_timer():
global timer
timer = True
hold_timer_start()
start_button['state'] = 'disabled'
def hold_timer_start():
global hold_time
global timer
if timer == True:
hold_time -= 1
hold_timer_label['text'] = hold_time
hold_timer_label.after(1000, hold_timer_start)
if hold_time == 0:
timer = False
def hold_timer_stop():
global hold_time
global timer
hold_time = 120
hold_timer_label['text'] = hold_time
timer = False
start_button['state'] = 'normal'
timer_frame = Frame(root, bg='gray25')
timer_frame.grid(row=0, column=0)
hold_timer_label = Label(timer_frame, text='Hold Time', bg='skyblue')
hold_timer_label.grid(row=0, column=1, sticky='ew', columnspan=2, pady=2)
hold_timer_label = Label(timer_frame, text=hold_time, bg='white')
hold_timer_label.grid(row=1, column=1, sticky='ew', columnspan=2, pady=2)
empty_label = Label(timer_frame, bg='gray25')
empty_label.grid(row=2, column=0)
start_button = Button(timer_frame, text='Start', bg='green', fg='black', command=start_timer)
start_button.grid(row=2, column=1)
stop_button = Button(timer_frame, text='Stop', bg='red', fg='black',command=hold_timer_stop)
stop_button.grid(row=2, column=2)
empty_label = Label(timer_frame, bg='gray25')
empty_label.grid(row=2, column=3)
root.mainloop()
I expect the timer to start counting down once the button is pressed but then do nothing if pressed again while the timer is counting down.
Unless the timer has hit zero or the stop button has been pressed.
You already have a timer flag set. All you need to do is to check it.
def start_timer():
global timer
if not timer:
timer = True
hold_timer_start()
#start_button['state'] = 'disabled'

Python timer in math game Tkinter

I'm looking to add a timer for my simple math game. So far everything works just fine, the user gets questions when pressing the button and is given feedback on the answer. I want to add a timer for the user to see how much time it takes to answer the multiplication. This is the final part of my prototype to this mathgame. I want the timer to start when the user clicks "nytt tal" which means new number in swedish, and to stopp when the user clicks "svar" which means answer in swedish. Here is my code.
from Tkinter import *
import tkMessageBox
import random
import time
import sys
# Definition for the question asked to user
def fraga1():
global num3
num3 = random.randint(1, 10)
global num4
num4 = random.randint(1, 10)
global svar1
svar1 = num3 * num4
label1.config(text='Vad blir ' + str(num3) + '*' + str(num4) + '?')
entry1.focus_set()
#The answer giving feedback based on answer
def svar1():
mainAnswer = entry1.get()
if len(mainAnswer) == 0:
tkMessageBox.showwarning(message='Skriv in några nummer!')
return
if int(mainAnswer) != svar1:
tkMessageBox.showwarning(message='Tyvärr det rätta svaret: ' + str(svar1))
else:
tkMessageBox.showinfo(message='RÄTT!! :)')
#The quit button definition
def quit():
global root
root.destroy()
#Definition for the timer this part doesnt work
def start():
global count_flag
fraga1()
count_flag = True
count = 0.0
while True:
if count_flag == False:
break
label['text'] = str(count)
time.sleep(0.1)
root.update()
count += 0.1
#Window code
root = Tk()
root.title("multiplikations tidtagning")
root.geometry('800x500')
count_flag = True
# Welcome message in labels
label2 = Label(root, text="Hej!\n Nu ska vi lösa lite matteproblem!")
label2.config(font=('times', 18, 'bold'), fg='black', bg='white')
label2.grid(row=0, column=0)
#Instructions how to play in labels
label3 = Label(root, text="Instruktioner!\n För att starta ett spel tryck på nyttspel")
label3.config(font=('times', 12, 'bold'), fg='black', bg='white')
label3.grid(row=2, column=2)
#other label
label1 = Label(root)
label1.grid(row=2, column=0)
# entry widget for the start button
entry1 = Entry(root)
entry1.grid(row=3, column=0)
# restart gives a new question
entry1.bind('', func=lambda e:checkAnswer())
#Buttons
fragaBtn = Button(root, text='Nytt tal', command=fraga1)
fragaBtn.grid(row=4, column=0)
svarButton = Button(root, text='Svar', command=svar1)
svarButton.grid(row=4, column=1)
quit_bttn = Button(root, text = "Avsluta", command=quit)
quit_bttn.grid(row=5, column=0)
root.mainloop()
I think what you need is this .
from Tkinter import *
import time
class StopWatch(Frame):
""" Implements a stop watch frame widget. """
def __init__(self, parent=None, **kw):
Frame.__init__(self, parent, kw)
self._start = 0.0
self._elapsedtime = 0.0
self._running = 0
self.timestr = StringVar()
self.makeWidgets()
def makeWidgets(self):
""" Make the time label. """
l = Label(self, textvariable=self.timestr)
self._setTime(self._elapsedtime)
l.pack(fill=X, expand=NO, pady=2, padx=2)
def _update(self):
""" Update the label with elapsed time. """
self._elapsedtime = time.time() - self._start
self._setTime(self._elapsedtime)
self._timer = self.after(50, self._update)
def _setTime(self, elap):
""" Set the time string to Minutes:Seconds:Hundreths """
minutes = int(elap/60)
seconds = int(elap - minutes*60.0)
hseconds = int((elap - minutes*60.0 - seconds)*100)
self.timestr.set('%02d:%02d:%02d' % (minutes, seconds, hseconds))
def Start(self):
""" Start the stopwatch, ignore if running. """
if not self._running:
self._start = time.time() - self._elapsedtime
self._update()
self._running = 1
def Stop(self):
""" Stop the stopwatch, ignore if stopped. """
if self._running:
self.after_cancel(self._timer)
self._elapsedtime = time.time() - self._start
self._setTime(self._elapsedtime)
self._running = 0
def Reset(self):
""" Reset the stopwatch. """
self._start = time.time()
self._elapsedtime = 0.0
self._setTime(self._elapsedtime)
def main():
root = Tk()
sw = StopWatch(root)
sw.pack(side=TOP)
Button(root, text='Start', command=sw.Start).pack(side=LEFT)
Button(root, text='Stop', command=sw.Stop).pack(side=LEFT)
Button(root, text='Reset', command=sw.Reset).pack(side=LEFT)
Button(root, text='Quit', command=root.quit).pack(side=LEFT)
root.mainloop()
if __name__ == '__main__':
main()
P.S: Fit this in your code I just implemented the basic timer in tkinter.
well if you're using a tkinter you're use the function
object.after(100, defineFunction)
the first parent is the milisecond
this'll apply to python3.x but 2.7 i cant be sure since i dont pratice that format
Use a global variable storing current time when person presses start. Then when the user presses svar in your function svar, just fetch current time, substract them from one another and you get time taken, then reset the global var to 0 also and voila, you have the time taken
I can suggest additional solution with threading.
I hold a small timer in one of my projects and it run as a different thread and gets updated each second:
import threading
import tkinter as tk
import time
root = tk.Tk()
timer = tk.Text(root, width=5, height=1)
timer.insert(tk.INSERT, "00:00")
timer.pack()
def show_time():
start = time.time()
seconds = 0
while True:
if time.time() - start > 1:
seconds += int(time.time() - start)
start = time.time()
cur_index = timer.index(tk.INSERT)
cur_index = str(int(cur_index[0]) - 1) + cur_index[1:]
timer.delete(cur_index, tk.INSERT)
timer.insert(tk.INSERT, str(int(seconds / 60)) + ":" + str(seconds % 60))
root.update()
thread = threading.Thread(target=show_time)
thread.start()
I think you can use "after".
You create a widget...
time = 60 #60 seconds for example
widget = Tkinter.Label().pack()
def count():
global time
if time > 0:
time -= 1
widget.config(text="Time left: " + str(time))
widget.after(1000, count)
I use python 3x
Sorry

Categories

Resources