Setting up Alarm notification with Popup

52 views
Skip to first unread message

Abraham Francisco

unread,
Jul 31, 2020, 4:19:50 PM7/31/20
to Kivy users support
Been quite a journey for me thus far and I'm finally getting to the meat and potatoes of what my app is supposed to do.

Would like to first off thank this forum for all the help thus far,

Where I'm at now, I've made an alarm method that in theory I thought would check the current time, check if it coincides with the set alarm time, then play a specific sound. Unfortunately, nothing happens. lol Not sure where to go next. Any suggestions on why things aint working?

Here's my code. I used a random .mp3 file as the loaded sound but left it blank in my code here just so you can use your own file.

 from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.properties import StringProperty
from kivy.uix.label import Label
from kivy.uix.screenmanager import Screen
from kivy.uix.image import Image
from kivy.uix.behaviors import ToggleButtonBehavior
import os
import time
import datetime
from kivy.uix.popup import Popup
from kivy.core.audio import SoundLoader

from time import strftime

kv = """
<Home@Screen>:
    BoxLayout:
        orientation: 'vertical'
        MyClock:
            font_size: 50
            text: self.current_time
        Button:
            background_color: (128,0,0,.6)
            background_down: '(0, 0, .3, 1)'
            text: 'Set Alarm'
            on_press: root.manager.current = 'alarm_page'

<AlarmPage@Screen>:
    GridLayout:
        rows: 4
        Button:
            text: 'Alarm 1'
            on_release: root.manager.current = 'edit1_page'

        Button:
            text: 'Alarm 2'
            on_release: root.manager.current = 'edit2_page'
        Button:
            text: 'Put alarms on this page'
            on_release: root.manager.current = 'home_page'

<AlarmEdit>:
    BoxLayout:
        orientation: 'vertical'
        GridLayout:
            cols: 4  
            Label:
                text: 'Set Time (00:00):'
            TextInput:
                id: alarmtime
                text: root.alarm_time
            ToggleButton:
                text: 'AM'
                group:'am/pm'
                state: root.alarm_am_state
                on_state: root.alarm_am_state = self.state
            ToggleButton:
                text: 'PM'
                group:'am/pm'
                state: root.alarm_pm_state
                on_state: root.alarm_pm_state = self.state
        Button:
            text: "Set Alarm"
            on_press: 
                root.set_alarm()
                root.manager.current = 'home_page'

BoxLayout:
    ScreenManager:
        id: sm
        Home:
            name: 'home_page'
        AlarmPage:
            name: 'alarm_page'
        AlarmEdit:
            name: 'edit1_page'
        AlarmEdit:
            name: 'edit2_page'
    """


class MyClock(Label):
    current_time = StringProperty(strftime("%I:%M:%S %p"))

    def __init__(self, **kwargs):
        Clock.schedule_interval(self.update_time, 1)
        super().__init__(**kwargs)

    def update_time(self, dt):
        self.current_time = strftime("%I:%M:%S %p")


class AlarmEdit(Screen):  # Moved to the screen
    alarm_time = StringProperty()
    alarm_am_state = StringProperty('down')  # set default values
    alarm_pm_state = StringProperty('normal')

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        if os.path.isfile("alarm1_details.txt"):
            with open("alarm1_details.txt", "r") as f:
                d = f.read().split(",")
                self.alarm_time = d[0]
                self.alarm_am_state = d[1]
                self.alarm_pm_state = d[2]

    def set_alarm(self):
        # am_pm = {'down': 'AM', 'normal': 'PM'}[self.alarm_am_state]
        self.alarm_time = self.ids.alarmtime.text + ':00'
        alarm_1 = self.alarm_time
        self.alarm(alarm_1)

        with open("alarm1_details.txt", "w") as f:
            f.write(f"{self.ids.alarmtime.text},{self.alarm_am_state},{self.alarm_pm_state}")
        # print(f'Alarm time set: {self.alarm_time}')

    def alarm(self, alarm_1):
        now = MyClock
        if now == alarm_1:
            popup = Popup(title='Alarm 1', content=Label(text=alarm_1), size_hint=(None, None), size=(400, 400))
            popup.open()
            sound = SoundLoader.load("")
            sound.play()


class MyButton(ToggleButtonBehavior, Label, Image):
    def __init__(self, **kwargs):
        super(MyButton, self).__init__(**kwargs)
        self.source = 'atlas://data/images/defaulttheme/checkbox_off'

    def on_state(self, widget, value):
        if value == 'down':
            self.source = 'atlas://data/images/defaulttheme/checkbox_on'
        else:
            self.source = 'atlas://data/images/defaulttheme/checkbox_off'


class MainApp(App):
    def build(self):
        return Builder.load_string(kv)


if __name__ == "__main__":
    MainApp().run()

Elliot Garbus

unread,
Jul 31, 2020, 6:12:00 PM7/31/20
to kivy-...@googlegroups.com

There are a few things to work here. 

The most important is where you are testing the for the alarm.

The code to check if an alarm time has been reached needs to go into your update_time method.  You are entering this method once per second to update the time, you also need to check if the alarm time has been reached here.

 

To access the alarm times from the update_time method you would do the following:

 

class MyClock(Label):

    current_time = StringProperty(strftime("%I:%M:%S %p"))

 

    def __init__(self, **kwargs):

        Clock.schedule_interval(self.update_time, 1)

        super().__init__(**kwargs)

 

    def update_time(self, dt):

        self.current_time = strftime("%I:%M:%S %p")

        app = App.get_running_app()

        t1 = app.root.ids.sm.get_screen(‘edit_1_page’).alarm(self.current_time) 

        # the alarm current code needs to change – you are passing in the current time, the alarm knows the alarm time.

        # make sure the format of the current_time and the alarm are the same for the comparison.

 

I also recommend you crete some content for your popup, at least a button that can dismiss the popup.

--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/377185b6-effc-4712-8327-908ffde32c5co%40googlegroups.com.

 

Abraham Francisco

unread,
Aug 1, 2020, 10:07:28 PM8/1/20
to kivy-...@googlegroups.com
So here's my attempt. I think I'm stuck in how to properly format in strftime my alarm_time. Added a few lines trying to feel my way to the answer but I'm still getting no sound and now I get error:

     self.passed_alarm = formatted_alarm.strftime("%I %M %S")
 AttributeError: 'list' object has no attribute 'strftime'

Code Below:
        app = App.get_running_app()
        t1 = app.root.ids.sm.get_screen('edit1_page').alarm(self.current_time)



class AlarmEdit(Screen):  # Moved to the screen
    alarm_time = StringProperty()
    alarm_am_state = StringProperty('down')  # set default values
    alarm_pm_state = StringProperty('normal')
    # formatted_alarm = StringProperty()
    passed_alarm = StringProperty()


    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        if os.path.isfile("alarm1_details.txt"):
            with open("alarm1_details.txt", "r") as f:
                d = f.read().split(",")
                self.alarm_time = d[0]
                self.alarm_am_state = d[1]
                self.alarm_pm_state = d[2]

    def set_alarm(self):
        # am_pm = {'down': 'AM', 'normal': 'PM'}[self.alarm_am_state]
        self.alarm_time = f"{self.ids.alarmtime.text}:00"
        formatted_alarm = self.alarm_time.split(":")
        self.passed_alarm = formatted_alarm.strftime("%I %M %S")
        # alarm_1 = self.alarm_time
        # self.alarm(alarm_1)


        with open("alarm1_details.txt", "w") as f:
            f.write(f"{self.ids.alarmtime.text},{self.alarm_am_state},{self.alarm_pm_state}")
        # print(f'Alarm time set: {self.alarm_time}')

    def alarm(self, current_time):
        if self.passed_alarm == current_time:
            popup = Popup(title='Alarm 1', content=Label(text=self.alarm_time), size_hint=(None, None), size=(400, 400))

            popup.open()
            sound = SoundLoader.load("")
            sound.play()


class MyButton(ToggleButtonBehavior, Label, Image):
    def __init__(self, **kwargs):
        super(MyButton, self).__init__(**kwargs)
        self.source = 'atlas://data/images/defaulttheme/checkbox_off'

    def on_state(self, widget, value):
        if value == 'down':
            self.source = 'atlas://data/images/defaulttheme/checkbox_on'
        else:
            self.source = 'atlas://data/images/defaulttheme/checkbox_off'


class MainApp(App):
    def build(self):
        return Builder.load_string(kv)


if __name__ == "__main__":
    MainApp().run()

Elliot Garbus

unread,
Aug 1, 2020, 10:46:01 PM8/1/20
to kivy-...@googlegroups.com
formatted_alarm = self.alarm_time.split(":")

 

Creates a list that contains the hours, minutes and seconds. 

Elliot Garbus

unread,
Aug 1, 2020, 11:22:52 PM8/1/20
to kivy-...@googlegroups.com

The code below works.  Here is a summary of the most significant changes.

 
def alarm(self, current_time):
        am_pm = {
'down': 'AM', 'normal': 'PM'}[self.alarm_am_state]
        a_time =
f'{self.alarm_time}:00 {am_pm}'
       
if a_time[1] == ':'# if the : is the second char,a leading zero is missing, insert it.
           
a_time = '0' + a_time
       
print(f'a: {a_time} ct: {current_time}'# test to watch the time...
       
if a_time == current_time:
            popup = Popup(
title='Alarm 1', content=Label(text=self.alarm_time), size_hint=(None, None), size=(400, 400))
            popup.open()
            sound = SoundLoader.load(
'Ring05.wav')
            sound.play()

The method alarm() is called every second by the update_time() method.

It constructs a string a_time that matches the formatting of the current_time.  The time can be entered with or without a leading zero.  If a ‘:’ is the second character in the string, a ‘0’ is prepended, to ensure the formatting matches.

The alarm time and the current time are printed to support debug.  The Popup comes up and the sound plays.

The set_alarm() method was simplified.

 

You might want to consider creating a text input widget that only allows a valid time to be entered.

 
Full code with update below:
 
-----
 
# am_pm = {'down': 'AM', 'normal': 'PM'}[self.alarm_am_state]
       
self.alarm_time = self.ids.alarmtime.text
       
# formatted_alarm = self.alarm_time.split(":")
        #
        # self.passed_alarm = formatted_alarm.strftime("%I %M %S")

        # alarm_1 = self.alarm_time
        # self.alarm(alarm_1)

       
with open("alarm1_details.txt", "w") as f:
            f.write(
f"{self.ids.alarmtime.text},{self.alarm_am_state},{self.alarm_pm_state}")
       
# print(f'Alarm time set: {self.alarm_time}')

   
def alarm(self, current_time):
        am_pm = {
'down': 'AM', 'normal': 'PM'}[self.alarm_am_state]
        a_time =
f'{self.alarm_time}:00 {am_pm}'
       
if a_time[1] == ':'# if the : is the second char,a leading zero is missing, insert it.
           
a_time = '0' + a_time
       
print(f'a: {a_time} ct: {current_time}'# test to watch the time...
       
if a_time == current_time:
            popup = Popup(
title='Alarm 1', content=Label(text=self.alarm_time), size_hint=(None, None), size=(400, 400))
            popup.open()
            sound = SoundLoader.load(
'Ring05.wav')
            sound.play()


class MyButton(ToggleButtonBehavior, Label, Image):
   
def __init__(self, **kwargs):
       
super(MyButton, self).__init__(**kwargs)
       
self.source = 'atlas://data/images/defaulttheme/checkbox_off'

   
def on_state(self, widget, value):
       
if value == 'down':
           
self.source = 'atlas://data/images/defaulttheme/checkbox_on'
       
else:
           
self.source = 'atlas://data/images/defaulttheme/checkbox_off'


class MainApp(App):
   
def build(self):
       
return Builder.load_string(kv)


if __name__ == "__main__":
    MainApp().run()


Sent: Saturday, August 1, 2020 7:07 PM
To: kivy-...@googlegroups.com

Abraham Francisco

unread,
Aug 1, 2020, 11:39:45 PM8/1/20
to kivy-...@googlegroups.com
I'm sure your code works buts I'm looking at mine and if formatted_alarm did equal the right time code and I made sure to always put a 0 before a single digits numbers when setting my alarm, then shouldn't my version work as well?

Abraham Francisco

unread,
Aug 1, 2020, 11:57:54 PM8/1/20
to kivy-...@googlegroups.com
Is adding in if it's am or pm necessary when matching time formats? Could that be why my code didn't work. Hmm

Elliot Garbus

unread,
Aug 2, 2020, 12:43:06 AM8/2/20
to kivy-...@googlegroups.com

The current time is formatted as:

09:34:10 PM

So we need to match that. 

 

You were making changes to set alarm, creating an attribute that would hold the time in a format to compare current time:

        formatted_alarm = self.alarm_time.split(":")

This will create a list of items separated at the ‘:’ or using 09:34:10 PM’

[‘09’, ‘34’, ’10 PM]  I’m not sure what you were trying here, perhaps you were going to convert hours and minutes to ints for the compare operation.


        self.passed_alarm = formatted_alarm.strftime("%I %M %S")

The line above was the line that was failing – I’m not sure what you were trying to do here.

Reply all
Reply to author
Forward
0 new messages