How to Pull toggle button info

53 views
Skip to first unread message

Abraham Francisco

unread,
Jul 8, 2020, 10:14:28 PM7/8/20
to Kivy users support
I'm trying to pull the entered info on my Alarm 1, in the 'set alarm' page.

How do I pull the info so I get hours, minutes, and am/pm toggle inputed with my set_button function.

Its an alarm app btw.

Code below:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.clock import Clock
from kivy.properties import StringProperty
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout

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: app.root.ids.sm.current = 'alarm_page'
<AlarmPage@Screen>:
GridLayout:
rows: 4
Button:
text: 'Alarm 1'
on_release: app.root.ids.sm.current = 'edit1_page'

Button:
text: 'Alarm 2'
Button:
text: 'Put alarms on this page'
on_release: app.root.ids.sm.current = 'home_page'
<Alarm1Edit@Screen>:
SetAlarm:
cols: 4
Label:
text: 'Set Time (00:00):'
TextInput:
id: alarmtime1
ToggleButton:
text: 'AM'
group:'am/pm'
ToggleButton:
text: 'PM'
group:'am/pm'
Button:
text: "Set Alarm"
on_press: set_button



BoxLayout:
ScreenManager:
id: sm
Home:
name: 'home_page'
AlarmPage:
name: 'alarm_page'
Alarm1Edit:
name: 'edit1_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 SetAlarm(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)

def set_button(self, instance):
alarmtime = self.alarmtime1.text


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


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

Elliot Garbus

unread,
Jul 9, 2020, 10:22:53 AM7/9/20
to kivy-...@googlegroups.com

Create a StringProperties in app for the data you want to share across screens.

Write the property in the place you are setting the alarm.

Assign it the text field (or state field for the toggle buttons)

 

Something like:


class MainApp342(App):
    alarm_1_time = StringProperty()
    alarm_1_am_state = StringProperty()
    alarm_1_pm_state = StringProperty()

 

Access in kv:

app.alarm_1_time

 

Access in Python (outside of App) as:

app = App.get_running_app()

app.alarm_1_time

--
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/26c92d3a-09f8-43a5-a7d9-42f621847a7eo%40googlegroups.com.

 

Abraham Francisco

unread,
Jul 9, 2020, 2:37:48 PM7/9/20
to kivy-...@googlegroups.com
This is what I understood from you:

            text: alarm_1_time
        ToggleButton:
text: 'AM'
group:'am/pm'
            state: alarm_1_am_state
        ToggleButton:
text: 'PM'
group:'am/pm'
            state: alarm_1_pm_state
        Button:
text: "Set Alarm"
on_press: set_button



BoxLayout:
ScreenManager:
id: sm
Home:
name: 'home_page'
AlarmPage:
name: 'alarm_page'
Alarm1Edit:
name: 'edit1_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 SetAlarm(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)

def set_button(self, instance):
alarmtime = self.alarmtime1.text

    alarm_1_time = StringProperty()
alarm_1_am_state = StringProperty()
alarm_1_pm_state = StringProperty()


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


app = App.get_running_app()
app.alarm_1_time


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

I got this error tho:

AttributeError: 'NoneType' object has no attribute 'alarm_1_time'

What did I misunderstand. Sorry for troubling.

Elliot Garbus

unread,
Jul 9, 2020, 5:09:15 PM7/9/20
to kivy-...@googlegroups.com

Close… more like this.  We are creating the string propery as a member of app to make it easy to access anywhere in the program.

 

 

class SetAlarm(GridLayout):

    def set_button(self, instance):

         app = App.get_running_app()
         app.alarm_1_time = self.alarmtime1.text


class MainApp(App):

    alarm_1_time = StringProperty()  #

    alarm_1_am_state = StringProperty()
    alarm_1_pm_state = StringProperty()


    def build(self):
        return Builder.load_string(kv)

…. And anywhere in kv as:  app.alarm_1_time

Abraham Francisco

unread,
Jul 13, 2020, 8:34:57 PM7/13/20
to kivy-...@googlegroups.com
So I tried it again with your corrections and now I get "NameError: name 'alarm_1_pm_state' is not defined"

I also have no idea where to put 'anywhere' that last correction of app.alarm_1_time. I put it in setalarm in kv above the col count but that would give me another error.

            app = App.get_running_app()
            app.alarm_1_time = self.alarmtime1.text
   
   
    class MainApp(App):
        alarm_1_time = StringProperty()
        alarm_1_am_state = StringProperty()
        alarm_1_pm_state = StringProperty()
   
        def build(self):
            return Builder.load_string(kv)
   
   
    if __name__ == "__main__":
        MainApp().run()

So I can't start my app now.

Elliot Garbus

unread,
Jul 14, 2020, 1:03:57 PM7/14/20
to kivy-...@googlegroups.com

Here is a working version.  I changed a number of things in the code, given what you were trying to achieve.  Notice I created on AlarmEdit screen.  I put the properties in the screen.  The screen is instanced twice to support your 2 alarms.

 

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 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>:
    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 set_alarm(self):
        am_pm = {
'down': 'AM', 'normal': 'PM'}[self.alarm_am_state]
        
self.alarm_time = self.ids.alarmtime.text + ' ' + am_pm
        
print(f'Alarm time set: {self.alarm_time}')


Abraham Francisco

unread,
Jul 16, 2020, 10:14:19 PM7/16/20
to kivy-...@googlegroups.com
You are too kind.

Can I ask what this section of code is doing though:

am_pm = {'down': 'AM', 'normal': 'PM'}[self.alarm_am_state]

Like that bracketed code and why specifically the am_state.

Thank you buddy.

Elliot Garbus

unread,
Jul 16, 2020, 10:37:59 PM7/16/20
to kivy-...@googlegroups.com
am_pm = {'down': 'AM', 'normal': 'PM'}[self.alarm_am_state]

 

That line is used to generate the AM or PM at the end of the time.

 

Breaking it down:

self.alarm_am_state is set to the value of the toggle button state in the kv code.

 

        ToggleButton:
           text: 'AM'
            group:'am/pm'
            state: root.alarm_am_state
            on_state: root.alarm_am_state = self.state

This value will be ‘down’ if it is AM, and ‘normal’ if the time is PM.  This is how state works in the ToggleButton.

 

This is a dictionary {'down': 'AM', 'normal': 'PM'}

So:

am_pm = {'down': 'AM', 'normal': 'PM'}[self.alarm_am_state]

This code uses self.alarm_am_state to index either ‘AM’ or ‘PM’. 

 

That one line is equivalent to:

If self.alarm_am_state == ‘down’

       am_pm =  ‘AM’

else:

       am_pm =  ‘PM’

Abraham Francisco

unread,
Jul 22, 2020, 12:37:35 PM7/22/20
to kivy-...@googlegroups.com
This helped me greatly. Thank you so much!

I decided I wanted to add a checkbox image to my toggle buttons using Image and ToggleButtonBahavior. My only problem is I cant seem to be able to show both the am or pm text and checkbox along together. If I remove the Label inheritance then the image shows fine but not the text.

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
        MyButton:

            text: 'AM'
            group:'am/pm'
            state: root.alarm_am_state
            on_state: root.alarm_am_state = self.state
        MyButton:
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()

Can you explain why this happens? I'm assuming as is, the label is completely blanketing the checkmark, but is there a way to move the text above the checkmark box without making a separate label in the kivy string? I guess basically, how to add text without using Label. Thank you Elliot!

Abraham Francisco

unread,
Jul 22, 2020, 12:39:29 PM7/22/20
to kivy-...@googlegroups.com
I'm sorry, disregard the above code.

Elliot Garbus

unread,
Jul 22, 2020, 4:23:48 PM7/22/20
to kivy-...@googlegroups.com

If you want to have a toggle button that has AM on it and a checkbox on it.  Open an image editor and create 2 images.

One with the AM in the button down color and the checkbox selected,  The other with AM the button normal color and the checkbox unselected.  And then another 2 images for PM.  Then create MyButton(ToggleBehavior, Image)

 

The images in the kivy default themes are located here: https://github.com/kivy/kivy/tree/master/kivy/tools/theming/defaulttheme

Abraham Francisco

unread,
Jul 23, 2020, 2:38:17 PM7/23/20
to kivy-...@googlegroups.com
How do I make so my 'set alarm' button here takes up the whole bottom row. Currently its a small 1/4 button to the left of the screen.

<AlarmEdit>:
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'

Elliot Garbus

unread,
Jul 23, 2020, 3:05:44 PM7/23/20
to kivy-...@googlegroups.com

To have the bottom button take the full width of the screen, you want it outside of the GridLayout.  Your could do this by nesting the GridLayout in a BoxLayout.  Something like:

 

<AlarmEdit>:
   
BoxLayout:
       
orientation: 'vertical'
       
GridLayout:
           
cols:
            
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:
           
size_hint_y: None
           
height: 48
           
text: "Set Alarm"
           
on_press:
                
root.set_alarm()
               
root.manager.current = 'home_page'

Abraham Francisco

unread,
Jul 25, 2020, 3:12:19 PM7/25/20
to kivy-...@googlegroups.com
Here's where Im at now Elliot. I went ahead and added code to be able to write down the alarm details in txt for saving purposes. On reboot tho, after pressing the alarm 1 button, it writes down in the time text input either the AM or PM and I don't want that or understand why that appears. How do I make it so that it doesn't write the am/pm in the text box on the edit page?

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
    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
    if os.path.isfile("alarm1_details.txt"):
        with open("alarm1_details.txt", "r") as f:
            d = f.read().split(",")
            alarm_time = StringProperty(d[0])
            alarm_am_state = StringProperty(d[1])  # set default values
            alarm_pm_state = StringProperty(d[2])
    else:

        alarm_time = StringProperty()
        alarm_am_state = StringProperty('down')  # set default values
        alarm_pm_state = StringProperty('normal')

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

        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}')



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()

Thank you again Elliot.

Elliot Garbus

unread,
Jul 25, 2020, 6:09:56 PM7/25/20
to kivy-...@googlegroups.com

Here are a few changes:

To remove the trailing AM/PM the set_alarm method has been simplified, so it no longer appends the AM/PM to the time.

I have also change the way you were declaring the string properties.  Kivy properties get declared like class properties and used as instance properties.

 

I also put the code that was in your alarm edit class, into a __init__ method.  You need a self pointer to properly address the string properties.

 

You may want to change your alarm_ properties to be a list properties and always load/store your 2 alarms to the file.  The way your code works now, the last alarm set will be the alarm written.  You are overwriting what exists in the file.

 

 

 

# 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# set default values
               
self.alarm_pm_state = d[2]

   
def set_alarm(self):
       
self.alarm_time = self.ids.alarmtime.text

Abraham Francisco

unread,
Jul 28, 2020, 12:42:01 AM7/28/20
to kivy-...@googlegroups.com
I plan to later make them into list properties but I just want a working alarm prototype first.

I added your changes but now I get this error on startup with a 'alarm1_details.txt' made in a prior boot:

ValueError: AlarmEdit.alarm_time accept only str

It's no longer reading my values in the text as strings. Do I need to switch to list properties to have it work again?

Elliot Garbus

unread,
Jul 28, 2020, 10:23:18 AM7/28/20
to kivy-...@googlegroups.com

The code works for me without error.

Did you change something?  Perhaps you need to delete the text file created with the previous version.

Reply all
Reply to author
Forward
0 new messages