0

I created a tkinter alarm clock which is not responding after setting the alarm. The alarm sound is playing but after that I have to exit the application and re-run the code to set alarm once again .I have shared the code and screenshot of the app.

from tkinter import 
import datetime
import time
from playsound import playsound

def Alarm(set_alarm_timer):
    while True:
        time.sleep(1)
        actual_time = datetime.datetime.now()
        cur_time = actual_time.strftime("%H:%M:%S")
        cur_date = actual_time.strftime("%d/%m/%Y")
        msg="Current Time: "+str(cur_time)
        print(msg)
        if cur_time == set_alarm_timer:
            playsound("Audio.mp3")
            break

def get_alarm_time():
    alarm_set_time = f"{hour.get()}:{min.get()}:{sec.get()}"
    Alarm(alarm_set_time)

window = Tk()
window.title("Alarm Clock")
window.geometry("400x160")
window.config(bg="#922B21")
window.resizable(width=False,height=False)

time_format=Label(window, text= "Remember to set time in 24 hour format!", fg="white",bg="#922B21",font=("Arial",15)).place(x=20,y=120)
addTime = Label(window,text = "Hour     Min     Sec",font=60,fg="white",bg="black").place(x = 210)
setYourAlarm = Label(window,text = "Set Time for Alarm: ",fg="white",bg="#922B21",relief = "solid",font=("Helevetica",15,"bold")).place(x=10, y=40)

hour = StringVar()
min = StringVar()
sec = StringVar()

hourTime= Entry(window,textvariable = hour,bg = "#48C9B0",width = 4,font=(20)).place(x=210,y=40)
minTime= Entry(window,textvariable = min,bg = "#48C9B0",width = 4,font=(20)).place(x=270,y=40)
secTime = Entry(window,textvariable = sec,bg = "#48C9B0",width = 4,font=(20)).place(x=330,y=40)

submit = Button(window,text = "Set Your Alarm",fg="Black",bg="#D4AC0D",width = 15,command = get_alarm_time,font=(20)).place(x =100,y=80)

window.mainloop()

enter image description here

Nithish M
  • 1
  • 1
  • 3
    Don't use `while True` loops or `time.sleep` when using `tkinter` unless you know all of the problems it can cause. Look at `.after` scripts. – TheLizzard Aug 12 '21 at 11:13
  • 1
    Does this answer your question? [How do you run your own code alongside Tkinter's event loop?](https://stackoverflow.com/questions/459083/how-do-you-run-your-own-code-alongside-tkinters-event-loop) – TheLizzard Aug 12 '21 at 11:13
  • 2
    You are using a `while` loop in your GUI program. You mustn't do that if you are not using threads otherwise your GUI will freeze as you can see. You should look into the `.after()` method to make something run later on. – quamrana Aug 12 '21 at 11:14
  • @quamrana You mentioned threads so I have to mention that `tkinter` isn't thread safe so it shouldn't be accessed from other threads. But I agree with everything you said. – TheLizzard Aug 12 '21 at 11:17
  • @TheLizzard: You are quite correct that `tkinter` is not thread safe. Don't use functions in multiple threads to update the gui elements. However, you can still use threads in a `tkinter` program as long as only the main thread updates the gui. The other threads can do whatever other processing they like, eg internet requests, database access, file processing. If any data needs to be returned to the GUI, then the data could be pushed into queues which the main thread services. – quamrana Aug 12 '21 at 11:22
  • @quamrana All calls to `tkinter` not just the ones that *update the gui elements*, should be in the same thread as the thread where you created the `tk.Tk()` window. It doesn't have to be in the main thread, but most of the time it's easier to just keep the GUI in the main thread. – TheLizzard Aug 12 '21 at 11:25
  • @TheLizzard: Yes, that sounds like a more precise version of what I said. – quamrana Aug 12 '21 at 11:27
  • @quamrana I think you might find [this](https://pastebin.com/sNijL6gm) code interesting. It doesn't even call any `tkinter` method from the other thread but still crashes with `Tcl_AsyncDelete`. – TheLizzard Aug 12 '21 at 11:31
  • Can someone explain what is threading? – Nithish M Aug 12 '21 at 11:31
  • @NithishM You shouldn't really use it when using `tkinter` unless you know what can happen, but threading is just a way to run 2 pieces of your code in parallel. – TheLizzard Aug 12 '21 at 11:32
  • I started coding python only two months ago. Can someone mention where to learn somewhat advanced python coding. – Nithish M Aug 12 '21 at 11:34
  • @NithishM [This](http://web.archive.org/web/20201111171246/https://effbot.org/tkinterbook/) is the unofficial `tkinter` documentation. Also I would suggest looking at some tutorials. I find that that's the easiest way to learn a new programming language. – TheLizzard Aug 12 '21 at 11:42
  • @TheLizzard I am asking for whole python course not specifically tkinter. If u can suggest anything then thank you. – Nithish M Aug 12 '21 at 12:16
  • Well I have only seen/participated in 1 course: [sololearn](https://www.sololearn.com/learning/1073). – TheLizzard Aug 12 '21 at 12:23

1 Answers1

1

.mainloop is some sort of while loop. So time.sleep() and while ... will mess with it. Use .after().


Edit: .place(),.pack() and .grid() geometry managers return None. And in python, the value of the last function is assigned.

here, it would be None, and might raise errors in future

def Alarm(set_alarm_timer):
    actual_time = datetime.datetime.now()
    cur_time = actual_time.strftime("%H:%M:%S")
    if cur_time != set_alarm_timer:
        msg="Current Time: "+str(cur_time)
        print(msg)
        window.after(1000,Alarm, set_alarm_timer)
    else:
        playsound("Audio.mp3")

...
submit = Button(window,text = "Set Your Alarm",fg="Black",bg="#D4AC0D",width = 15,command = lambda: Alarm(f"{hour.get()}:{min.get()}:{sec.get()}"),font=(20))
submit.place(x =100,y=80)

  • the only slight issue with using `.after` is that they are not that precise, so for making a clock they are not particularly great (unless you create a rather small algorithm that compensates for taking longer or shorter times to execute the scheduled function) – Matiiss Aug 12 '21 at 11:20
  • @Matiiss maybe use something like ```.after(995)``` or something that can be rounded off to ```1000```? –  Aug 12 '21 at 11:22
  • 1
    First of all you, didn't mention the `submit = Button(...).place()` issue. Second of all, why not calculate the difference in time and then schedule only 1 call to `Alarm`. Third of all, instead of `window.after(1000,lambda:Alarm(set_alarm_timer))` use `window.after(1000, Alarm, set_alarm_timer)` – TheLizzard Aug 12 '21 at 11:22
  • @TheLizzard Thanks for the answer. Just curious to know, why to use ```window.after(1000, Alarm,set_alarm_timer)``` instead of ```window.after(1000,lambda:Alarm(set_alarm_timer))```, –  Aug 12 '21 at 11:26
  • 1
    @Sujay When you use `lambda`, it creates an object in memory also not 100% sure but I think it keeps most variables from going out of scope. That can increase memotry usage but not my much. I personally find it cleaner to use `.after(1000, Alarm, set_alarm_timer)` – TheLizzard Aug 12 '21 at 11:28
  • I started coding python only two months ago. Can someone mention where to learn somewhat advanced python coding. – Nithish M Aug 12 '21 at 11:40