-4
from tkinter import *
from tkinter import messagebox
import pygame
import time
pygame.init()
def alarm_time():
    hrs = int(hrs_opt_ctrl.get())
    min = int(min_opt_ctrl.get())
    tme = tme_ctrl.get()
    if hrs == int(time.strftime('%I')) and min == int(time.strftime('%M')) and tme == time.strftime('%p'):
        # Time is up. Play the sound
        alarm_ringtone = pygame.mixer.music.load('alarm_noise.mp3')
        pygame.mixer.music.play()
        # Don't call after again
    else:
        # Not time, sleep for 1 second
        window.after(1000, alarm_time)

def snooze_time():
    snoz_min=(2,5,10,20,30,35,40,45,50,55,59)
    snooze=True
    while snooze:
        try:
            min = min + int(snoz_min[minute])
            window.after((min)*1000,alarm_time)
        except:
            messagebox.showerror("Error 404", "Cannot Snooze for given Time")
        if minute<=len(snoz_min):
            minute+=1
        elif minute!=len(snoz_min):
            minute=0
        else:
            pass
    snooze=False

window=Tk()
window.title('Alarm')
window.config(background='black')
logo=PhotoImage(file='alarm.gif')
lab_1=Label(window,text='Alarm',bg='black',fg='white',font=('Times',25,'bold')).grid(column=100,row=0)
lab_2=Label(window,bg='black',image=logo).grid(column=300,row=0)
lab_3=Label(window,text='Hours',bg='black',fg='white',font=('Comic',10,'bold')).grid(column=50,row=130)
lab_4=Label(window,text='Minutes',bg='black',fg='white',font=('Comic',10,'bold')).grid(column=85,row=130)
opt_hrs=[]
opt_min=[]
opt_tme=('AM','PM')
minute=0
for i in range(1,13):
    opt_hrs.append(i)
for j in range(0,60):
    opt_min.append(j)
hrs_opt_ctrl=StringVar()
min_opt_ctrl=StringVar()
tme_ctrl=StringVar()
tme_ctrl.set(opt_tme[0])
hrs_lab=OptionMenu(window,hrs_opt_ctrl,*opt_hrs).grid(column=60,row=130,columnspan=15)
min_lab=OptionMenu(window,min_opt_ctrl,*opt_min).grid(column=86,row=130,columnspan=15)
tme_lab=OptionMenu(window,tme_ctrl,*opt_tme).grid(column=150,row=130)
but_1=Button(window,text='Set Alarm',font=('Comic',10,'bold'),command=alarm_time).grid(column=100,row=240)
but_2=Button(window,text='Snooze',font=('Comic',10,'bold'),command=snooze_time).grid(column=100,row=250)
window.mainloop()

The following code doesnt show any error when run but doesnt play the alarm. The alarm file is in .mp3 extension and in the same folder as the project. i probably think that something's wrong in the time module code or the 'Set Alarm' button is not taking any input.The position of columns and rows are not accurate

  • Looks like you need to press the button at the time the alarm is set for. Are you doing that? [How to create a timer using tkinter?](//stackoverflow.com/q/2400262) – 001 Nov 15 '19 at 13:14
  • yes i am pressing the button but no response of any sort – Prometheus_26 Nov 15 '19 at 13:19
  • What have you done to debug this? Have you verified that `hrs`, `min`, and `tme` are what you think they are? Also, why do you expect it to play? It looks like you've designed it to play only if you click the button at the exact correct time. – Bryan Oakley Nov 15 '19 at 13:23
  • Oh didnt know that.Is there any way to correct this? – Prometheus_26 Nov 15 '19 at 13:26
  • @4shwin_26 Sorry my bad remove `\ `. Should read: Add `print('{}=={} and {}=={} and {}=={}'.format(hrs, time.strftime('%I'), min, time.strftime('%M'), tme, time.strftime('%p')))` before your `if ...` condition. – stovfl Nov 15 '19 at 13:34
  • so how can i fix it so that the alarm runs at the desired time? – Prometheus_26 Nov 15 '19 at 13:44
  • @4shwin_26 First you have to understand [Event-driven programming](https://stackoverflow.com/a/9343402/7414759). You have to run `def alarm_time():` every minute. – stovfl Nov 15 '19 at 13:46
  • so basically after is used to check something in an interval and change it.am i right? – Prometheus_26 Nov 15 '19 at 14:02
  • @4shwin_26 ***"how after can that help this code?"***: It is ***"used to check something in an interval"***, read [Tkinter.Widget.after-method](http://effbot.org/tkinterbook/widget.htm#Tkinter.Widget.after-method) ***"i am not changing anything"***: You don't need to change anything. – stovfl Nov 15 '19 at 14:10
  • @4shwin_26 ***"if ive to write `window.after(1000, alarm_time)`"***: Yes, if you want to check every **Second**, keep in mind, if your conditon `if hrs== ...` becomes `True` you have to end calling `.after(...`. – stovfl Nov 15 '19 at 14:23
  • @4shwin_26 ***"how do i stop calling after?"***: You don't have to stop, `.after(...` it is a **one time** callback. It stops if you don't call it any more. – stovfl Nov 15 '19 at 14:43
  • just asking, is there any way to optimize the code further? like adding multiple alarms snoozing ? – Prometheus_26 Nov 15 '19 at 15:07

1 Answers1

2

As mentioned in the comments, you have it setup to only play the alarm iff the user clicks the button at the time the alarm is set to. Instead, you should check the time every second, and play the alarm at the given time:

def alarm_time():
    hrs = int(hrs_opt_ctrl.get())
    min = int(min_opt_ctrl.get())
    tme = tme_ctrl.get()
    if hrs == int(time.strftime('%I')) and min == int(time.strftime('%M')) and tme == time.strftime('%p'):
        # Time is up. Play the sound
        alarm_ringtone = pygame.mixer.music.load('alarm_noise.mp3')
        pygame.mixer.music.play()
        # Don't call after again
    else:
        # Not time, sleep for 1 second
        window.after(1000, alarm_time)

Note, I convert everything to int because time.strftime('%I') and time.strftime('%M') return 0-pre-padded strings and your option box does not have 0-pre-padded values, so "1" != "01", etc.


Updated answer with multiple alarms and snooze. I just threw this together so there is plenty of room for improvement:

from tkinter import *
import pygame
import time

pygame.mixer.init()

window = Tk()
window.geometry('300x200')
window.title('Alarm')

#logo = PhotoImage(file='alarm.gif')

lab_1 = Label(window, text='Alarm Clock', font=('Times',20,'bold')).grid(column=200, row=0)
#lab_2=Label(window,image=logo).grid(column=300,row=0,columnspan=3)
lab_3 = Label(window, text='Hours', font=('Comic',10,'bold')).grid(column=50,row=10, columnspan=3)
lab_4 = Label(window, text='Minutes', font=('Comic',10,'bold')).grid(column=90,row=10, columnspan=3)

# Alarm class
class Alarm:
    alarm_id = 1
    def __init__(self, hours, minutes, ampm, sound_file):
        self.sound_file = sound_file
        # Convert hours, minutes, ampm to a timestamp
        # Save time as a timestamp
        t = time.localtime()
        t = time.strptime(f"{t.tm_year}-{t.tm_mon}-{t.tm_mday} {hours} {minutes} {ampm}", "%Y-%m-%d %I %M %p")
        self.alarm_time = time.mktime(t)
        # Number of seconds to snooze
        self.snooze_time = None
        self.completed = False   # Set to True after alarm has gone off
        self.id = Alarm.alarm_id
        Alarm.alarm_id += 1

    # Every time this is called, it checks the time to see if the alarm should go off
    def check_time(self, cur_time):
        # Use alarm time or snooze time?
        on_time = self.snooze_time if self.snooze_time else self.alarm_time
        # Since might not be called when seconds is 0, check if it is with one minute of alarm time
        time_diff = cur_time - on_time
        if not self.completed and time_diff >= 0 and time_diff < 60:
            self.completed = True
            alarm_ringtone = pygame.mixer.music.load(self.sound_file)
            pygame.mixer.music.play()
        # Reset after 30 minutes - add 24 hours (daily timer)
        elif self.completed and time_diff > 1800 and time_diff < 1860:
            self.completed = False
            self.snooze_time = None
            self.alarm_time += 24 * 60 * 60

    def snooze(self, minutes):
        if self.completed and pygame.mixer.music.get_busy():
            self.snooze_time = time.time() + (minutes * 60)
            self.completed = False
            pygame.mixer.music.stop()

    # Convert to string for printing
    def __str__(self):
        id_str = f"[{self.id}]: "
        if self.completed:
            return id_str + ("Alarm in progress" if pygame.mixer.music.get_busy() else "Alarm completed")
        elif self.snooze_time:
            time_left = int(self.snooze_time - time.time())
            minutes = time_left // 60
            seconds = time_left % 60
            if minutes:
                return id_str + f"Snoozing for {minutes} minutes and {seconds} seconds"
            else:
                return id_str + f"Snoozing for {seconds} seconds"
        else:
            return id_str + f"Alarm set for {time.ctime(self.alarm_time)}"

# This list holds all alarms
all_alarms = []

# Tell all alarms to check the time
def check_alarms():
    now = time.time()
    for alarm in all_alarms:
        print(f"Checking: {alarm}");
        alarm.check_time(now)
    # Call again after 1 second
    window.after(1000, check_alarms)

# Creates a single object of the Alarm class
# Uses values from the option controls
def create_alarm():
    hours = int(hrs_opt_ctrl.get())
    minutes = int(min_opt_ctrl.get())
    ampm = tme_ctrl.get()
    alarm = Alarm(hours, minutes, ampm, "alarm.mp3")
    all_alarms.append(alarm)
    print(f"Adding: {alarm}");

# Snoozes all active alarms for 2 minutes
def snooze_current():
    for alarm in all_alarms:
        alarm.snooze(2)

but = Button(window, text='Set Alarm', font=('Comic',10,'bold'), command=create_alarm).grid(column=100,row=15)
snooze = Button(window, text='Snooze', font=('Comic',10,'bold'), command=snooze_current).grid(column=100,row=16)
opt_hrs = []
opt_min = []
opt_tme = ('AM','PM')
for i in range(1,13):
    opt_hrs.append(i)
for j in range(0,60):
    opt_min.append(j)

hrs_opt_ctrl = StringVar()
min_opt_ctrl = StringVar()
tme_ctrl = StringVar()
hrs_lab = OptionMenu(window, hrs_opt_ctrl, *opt_hrs).grid(column=70,row=10,columnspan=3)
min_lab = OptionMenu(window, min_opt_ctrl, *opt_min).grid(column=100,row=10,columnspan=3)
tme_lab = OptionMenu(window, tme_ctrl, *opt_tme).grid(column=120,row=10,columnspan=3)

# Fill with default values of current time
hrs_opt_ctrl.set(str(int(time.strftime('%I'))))
min_opt_ctrl.set(str(int(time.strftime('%M'))))
tme_ctrl.set(time.strftime('%p'))

check_alarms()
window.mainloop()
pygame.mixer.music.stop()
001
  • 13,291
  • 5
  • 35
  • 66
  • ohhhhhhhhhhhhh!!!! Thanks mate!! – Prometheus_26 Nov 15 '19 at 14:46
  • just asking, is there any way to optimize the code further? like adding multiple alarms snoozing ? – Prometheus_26 Nov 15 '19 at 15:04
  • @4shwin_26 For a simple program like this, there's probably not much to do to optimize it. One thing is to modify the time the program sleeps based on how much time until the alarm. For example: if the alarm is 1 hour away, sleep for 59 minutes but if the alarm is 5 min sleep for 4 minutes, etc. Note there is another site, [codereview.se], where you can post **working** code and have others review it and offer suggestions. Also, _adding multiple alarms snoozing_ is not optimization. They are features you can add. – 001 Nov 15 '19 at 15:07
  • can you give me an idea on how to snooze and add multiple alarms? – Prometheus_26 Nov 15 '19 at 15:10
  • @4shwin_26 Snooze would be simple enough: add a snooze button, then add a few minutes to the alarm time when the user clicks snooze, (don't forget to call `after` again to restart the timer). Multiple alarms will be more complex. Are you familiar with classes? – 001 Nov 15 '19 at 15:13
  • i just started learning classes but i think i know the basics – Prometheus_26 Nov 15 '19 at 15:21
  • also how do i fill the image(resizing the window shouldn't affect the position of the image) – Prometheus_26 Nov 15 '19 at 15:24
  • @4shwin_26 Back from lunch. Updated with snooze and multiple timers. – 001 Nov 15 '19 at 18:39
  • Thanks.Just a question is function inheritances possible ? – Prometheus_26 Nov 16 '19 at 14:28
  • also can u explain the time functions used? – Prometheus_26 Nov 16 '19 at 14:49
  • @4shwin_26 _Class_ inheritance is definitely possible. There are tons of tutorials about that online. In this app I chose to store the time as a `float` value (the number of seconds since 1/1/1970). This is what the [`time.time()`](https://docs.python.org/3/library/time.html#time.time) function returns. Some other functions return a [`struct_time`](https://docs.python.org/3/library/time.html#time.struct_time) so a conversion is required. – 001 Nov 18 '19 at 13:02
  • any simpler form without classes? – Prometheus_26 Nov 19 '19 at 20:33