You want to use the name of the function in the on_press, if the function has the () then you are executing the function at the time you are doing the assignment and assigned the returned value to the on_press.
You could use functools partial or a lambda to set the callback. I find it most coinvent to use lambda. Example below.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from functools import partial
kv = """
BoxLayout:
orientation: 'vertical'
"""
class BindButtonApp(App):
def build(self):
return Builder.load_string(kv)
def callback(self, message, widget):
print(message)
def on_start(self):
p = partial(self.callback, "the parameter in a partial")
button = Button(text='use partial', on_release=p)
self.root.add_widget(button)
button = Button(text='use lambda', on_release=lambda x: print('Used lambda'))
self.root.add_widget(button)
BindButtonApp().run()
--
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/18aea999-d192-4e00-9c8c-a95c1efc03d9n%40googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/640233d8.4a0a0220.6c9de.25acSMTPIN_ADDED_MISSING%40gmr-mx.google.com.
I hope the discussion below helps. I’m trying to answer your question, and offer explanations and options. Let me know if this cause more confusion than illumination.
If you look at the documentation for bind: https://kivy.org/doc/stable/api-kivy.event.html?highlight=bind#kivy.event.EventDispatcher.bind
You see that:
In general, property callbacks are called with 2 arguments (the object and the property’s new value) and event callbacks with one argument (the object).
The on_press method is being called from the event loop, and it is being called with the object that caused the event.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import
Button
kv = """
BoxLayout:
orientation: 'vertical'
TestButton:
text: 'Button 1'
TestButton:
text: 'Button 2'
"""
class TestButton(Button):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.bind(on_press=self.on_press_cb)
self.bind(on_release=self.on_release_cb)
def on_press_cb(self, button):
print(button, button.text)
# this is redundant because we can do the same with self
print(self, self.text)
def on_release_cb(self, _):
# we can use _ to indicate we are not using the returned value
print(f'{self.text} released')
class BindButtonApp(App):
def build(self):
return
Builder.load_string(kv)
BindButtonApp().run()
If you want to pass an argument to the callback there are 4 options.
Here is an example of each type
from functools import partial
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import
Button
kv = """
<TestButtonKV>:
on_press: self.on_press_cb('You can easily pass the message from kv')
on_release: self.on_release_cb('You can also access the args from kv', *args)
BoxLayout:
orientation: 'vertical'
TestButtonPartial:
text: 'Button Partial'
TestButtonLambda:
text: 'Button Lambda'
TestButtonStub:
text: 'Button Stub'
TestButtonKV:
text: 'Button KV'
"""
class TestButtonPartial(Button):
def __init__(self, **kwargs):
super().__init__(**kwargs)
p = partial(self.on_press_cb, "Callback message with partial")
self.bind(on_press=p)
def on_press_cb(self, message, obj): # obj os the same as self, so I would typically make this a _
print(self, obj, self.text, message, obj)
class TestButtonLambda(Button):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.bind(on_press=lambda obj: self.on_press_cb("Callback message from lambda"))
# the on_press event is called and passes obj to the lambda function, it does not pass it on
# in the on_release, we forward the obj to the on_release_cb
# there is no real need to do this - I'm just showng how a lambda works.
self.bind(on_release=lambda obj: self.on_release_cb("passing a message and the obj", obj))
def on_press_cb(self, message): # obj os the same as self, so I would typically make this a _
print(self, self.text, message)
def on_release_cb(self, message, obj):
print(self, self.text, message, obj)
class TestButtonStub(Button):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.bind(on_press=self.on_press_cb)
def on_press_cb(self, _):
# not parameter is passed, create a seperate method for each paramater you want passed..
# you might for example create a stub for each screen you want to change to (yuck).
print(f'{self.text} No parameter was passed')
class TestButtonKV(Button):
def on_press_cb(self, message):
print(f'{self.text} {message}')
def on_release_cb(self, message, obj):
print(f'{self.text} {message} {obj}')
class BindButtonApp(App):
def build(self):
return
Builder.load_string(kv)
BindButtonApp().run()To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/CAAT1hNC%3D4kbVzeEL%3DzYHZ_aewHFrOMHT8u06AXTt%2Bd2yS6z-MQ%40mail.gmail.com.
I did not answer this questions in my longer response:
The "bind to the execution" dialect: the "on_press" is in a bind statement and uses the method+parameters as an execution call
=====================================================================================================
def __init__(self ...
...
self.exit_button = Button(text="Regresar", size_hint=(1, 0.3))
self.exit_button.bind(on_press=self.switch_screens('home')
def switch_screens(self, switch_to, *args):
print(f"PhotoScreen2: switch_screens: {switch_to})")
if switch_to == 'home':
print(f"PhotoScreen2: switch_screens({switch_to})")
self.screen_manager.current = '0'
This one, the button reacts but nothing happens:
In the line:
self.exit_button.bind(on_press=self.switch_screens('home'))
The call to the method self.switch_screens(‘home’) is made when the code is executed, and self.switch_screens(‘home’) returns None.
This is equivalent to: self.exit_button.bind(on_press=None)
From: Gary Kuipers
Sent: Saturday, March 4, 2023 12:54 PM
To: kivy-...@googlegroups.com
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/CAAT1hNC%3D4kbVzeEL%3DzYHZ_aewHFrOMHT8u06AXTt%2Bd2yS6z-MQ%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/6403db88.e90a0220.1011a.b178SMTPIN_ADDED_MISSING%40gmr-mx.google.com.
class BindButtonApp(App):
def build(self):
return Builder.load_string(kv)
On Mar 5, 2023, at 3:43 AM, Gary Kuipers <gary.k...@casinfosystems.com> wrote:
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/CAAT1hNCDsAaGmuwk4a6Y5mVkTV1vBYR2mNQ49sJJPb8P%2B05FHg%40mail.gmail.com.
I’m not sure exactly the question you are asking – my interpretation is you are trying to set the value of a property that is in ButtonLayout1 from the App class.
I stripped down the code to fit in one file, but maintained the same widget hierarchy.
In MyApp on_start, I show how to access properties in ButtonLayout1. Notice in ButtonLayout1 I have added a property called gk, it is used to set the text on one of the buttons.
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.utils import platform
PS1 = """
<PhotoScreen1>:
photo_preview: photo_layout.ids.preview
PhotoLayout1:
id:photo_layout
"""
class PhotoScreen1(Screen):
photo_preview = ObjectProperty(None)
def __init__(self, **args):
Builder.load_string(PS1)
super().__init__(**args)
# def on_enter(self):
# self.photo_preview.connect_camera(filepath_callback=self.capture_path)
#
# def on_pre_leave(self):
# self.photo_preview.disconnect_camera()
PL1 = """
<PhotoLayout1>:
Background1:
id: pad_end
Button: # Preview:
id: preview
# letterbox_color: .8, .3, 1, .5
ButtonsLayout1:
id: buttons
<Background1@Label>:
canvas:
Color:
rgba: .8, .3, 1, .5
Rectangle:
pos: self.pos
size: self.size
"""
class PhotoLayout1(BoxLayout):
def __init__(self, **args):
Builder.load_string(PL1)
super().__init__(**args)
def on_size(self, layout, size):
if Window.width < Window.height:
self.orientation = 'vertical'
self.ids.preview.size_hint = (1, .8)
self.ids.buttons.size_hint = (1, .2)
self.ids.pad_end.size_hint = (1, .1)
else:
self.orientation = 'horizontal'
self.ids.preview.size_hint = (.8, 1)
self.ids.buttons.size_hint = (.2, 1)
self.ids.pad_end.size_hint = (.1, 1)
BL1 = """
<ButtonsLayout1>:
Background1:
Button:
id:other
text: root.gk
# on_press: root.select_camera('toggle')
height: self.width
width: self.height
# background_normal: 'icons/camera-flip-outline.png'
# background_down: 'icons/camera-flip-outline.png'
Button:
id:flash
# on_press: root.flash()
height: self.width
width: self.height
# background_normal: 'icons/flash-off.png'
# background_down: 'icons/flash-off.png'
Button:
id:photo
# on_press: root.photo()
height: self.width
width: self.height
# background_normal: 'icons/camera_white.png'
# background_down: 'icons/camera_red.png'
"""
HS0 = """
<HomeScreen0>:
AnchorLayout:
Button:
size_hint: None, None
size: dp(200), dp(48)
on_release: root.manager.current = root.manager.next()
text: 'Next'
"""
class HomeScreen0(Screen):
def __init__(self, **kwargs):
Builder.load_string(HS0)
super().__init__(**kwargs)
class ButtonsLayout1(RelativeLayout):
gk = StringProperty()
def __init__(self, **args):
Builder.load_string(BL1)
super().__init__(**args)
def on_size(self, layout, size):
if platform in ['android', 'ios']:
self.ids.photo.min_state_time = 0.3
else:
self.ids.photo.min_state_time = 1
if Window.width < Window.height:
self.ids.other.pos_hint = {'center_x': .2, 'center_y': .5}
self.ids.other.size_hint = (.2, None)
self.ids.photo.pos_hint = {'center_x': .5, 'center_y': .5}
self.ids.photo.size_hint = (.24, None)
self.ids.flash.pos_hint = {'center_x': .8, 'center_y': .5}
self.ids.flash.size_hint = (.15, None)
else:
self.ids.other.pos_hint = {'center_x': .5, 'center_y': .8}
self.ids.other.size_hint = (None, .2)
self.ids.photo.pos_hint = {'center_x': .5, 'center_y': .5}
self.ids.photo.size_hint = (None, .24)
self.ids.flash.pos_hint = {'center_x': .5, 'center_y': .2}
self.ids.flash.size_hint = (None, .15)
class MyApp(App):
def build(self):
self.enable_swipe = False
self.sm = ScreenManager()
self.screens = [HomeScreen0(name='0'),
PhotoScreen1(name='1')]
for s in self.screens:
self.sm.add_widget(s)
return self.sm
def on_start(self):
# set text of flash button, using ids to 'walk' the widget hierarchy
self.root.get_screen('1').ids.photo_layout.ids.buttons.ids.flash.text = 'Flash'
# write to a property in ButtonLayout1
self.root.get_screen('1').ids.photo_layout.ids.buttons.gk = 'The gk property'
MyApp().run()
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/CAAT1hNCDsAaGmuwk4a6Y5mVkTV1vBYR2mNQ49sJJPb8P%2B05FHg%40mail.gmail.com.
def build(self):
logger = Logger()
self.enable_swipe = False
self.sm = MyScreenManager()
home_screen = HomeScreen0(filemanager, logger.log, name='0')
self.sm.add_widget(home_screen)
photo_screen = PhotoScreen1(self.sm, filemanager, name='1')
photo_screen.screen_manager = self.sm
self.sm.add_widget(photo_screen)
review_screen = PhotoScreen2(self.sm, filemanager, name='2')
review_screen.screen_manager = self.sm
self.sm.add_widget(review_screen)
# ps1 = PhotoScreen1(name='1')
# self.sm.add_widget(ps1)
if platform == 'android':
Window.bind(on_resize=hide_landscape_status_bar)
return self.sm
class PhotoScreen1(Screen):
photo_preview = ObjectProperty(None)
def __init__(self, screen_manager, filemanager, **kwargs):
Builder.load_string(PS1)
super().__init__(**kwargs)
self.screen_manager = screen_manager
self.filemanager = filemanager
print(f"PhotoScreen1: Init")
def on_enter(self):
self.screen_manager = self.manager
self.photo_preview.connect_camera(filepath_callback=self.capture_path)
# BL0 = """
# <ButtonsLayout0>:
# canvas.before:
# Color:
# rgba: 1, 1, 1, 1
# Rectangle:
# size: self.size
# pos: self.pos
# GridLayout:
# cols: 2
# Image:
# source: 'logo_transparent.png'
# size_hint: .6, 1
# background_color: 0, 0, 1, 1
# Button:
# id: btnExit
# canvas.before:
# Color:
# rgba: 1, .647, 0, 1
# Line:
# width: 8
# rectangle: self.x, self.y, self.width, self.height
# background_color: 0, 0, 0, 0
# text: "REGRESAR"
# bold: True
# #on_press: app.stop()
# on_press: root.screen_manager.current = '0'
# size_hint: .4, 1
# """
# class ButtonsLayout0(RelativeLayout):
# def __init__(self, **args):
# super().__init__(**args)
# Builder.load_string(BL0)
#
# TODO: should go back to homescreen 0
class ButtonsLayout0(RelativeLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# with self.canvas.before:
# Color(1, 1, 1, 1)
# self.rect = Rectangle(size=self.size, pos=self.pos)
self.grid = GridLayout(cols=2, size_hint=(1, 1))
self.add_widget(self.grid)
self.image = Image(source='logo_transparent.png', size_hint=(0.6, 1))
self.grid.add_widget(self.image)
self.btnExit = Button(background_color=(0, 0, 0, 0), text="REGRESAR", bold=True, size_hint=(0.4, 1))
# with self.btnExit.canvas.before:
# Color(1, 0.647, 0, 1)
self.btnExit.bind(on_press=lambda instance: setattr(self.screen_manager, 'current', '0'))
self.grid.add_widget(self.btnExit)
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/6404cb76.050a0220.62348.d706SMTPIN_ADDED_MISSING%40gmr-mx.google.com.
You don’t need to pass the screenmanager through all those classes, although I’ll show you how to do that in follow up message.
You know the ScreenManager is the root widget. In python in the App class, you can access the ScreenManager as self.root.
In any other python class you can access the screenmanager (the root widget) as follows
class MyClass(MyWidet):
def my_method(self):
app = App.get_running_app() # returns the app instance
app.root.current = ‘0’ # will change to screen ‘0’
In kv you can simply access as:
Button:
on_release: app.root.current = ‘0’
Be carful copying this code – the ‘’ are the wrong type for code.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/CAAT1hNAYr_Ny4fDNvjGGLv6PvfmZ0GCuMi-1bY5gZ%2B9uODRxkw%40mail.gmail.com.
Here are some alternate ways to access the screenmanager.
In kv, I would use app.root to access the sm. This is most directy, and easy to maintain. This is how I would do it.
I showed a number of alternatives:
The ButtonsLayout1 is instanced in kv, you could create a kivy property in the python code for the class, and then set that property in the kv code where ButtonLayout1 is instanced. In this context “root” is PhotoLayout1, “root.parent” is PhotoScreen1, a Screen has a “manager” attribute. The manager attribute is the ScreenManager the screen is under.
Another approach would be to “flatten” the screen definition, so the code under the screen was not so deeply nested. If this were the case you could access the screen manager as “root.manager “ anywhere under that Screen.
Review the code below and let me know what your think.
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.utils import platform
PS1 = """
<PhotoScreen1>:
photo_preview: photo_layout.ids.preview
PhotoLayout1:
id:photo_layout
"""
class PhotoScreen1(Screen):
photo_preview = ObjectProperty(None)
def __init__(self, **args):
Builder.load_string(PS1)
super().__init__
(**args)
PL1 = """
<PhotoLayout1>:
Background1:
id: pad_end
Button: # Preview:
id: preview
# letterbox_color: .8, .3, 1, .5
ButtonsLayout1:
id: buttons
screen_manager: root.parent.manager # set the screen_manager property""" text: 'Screen 0 with KV'
on_press: app.root.current = '0' # access the sm directly, THIS IS WHAT I WOULD DO
height: self.width
width: self.height
# background_normal: 'icons/camera-flip-outline.png'
# background_down: 'icons/camera-flip-outline.png'
Button:
id:flash
height: self.width
width: self.height
text: 'Screen 0 using property'
on_press: root.screen_manager.current = '0' # use the kivy property
# background_normal: 'icons/flash-off.png'
# background_down: 'icons/flash-off.png'
Button:
id:photo
text: 'using python'
on_press: root.go_home()
height: self.width
width: self.height
# background_normal: 'icons/camera_white.png'
# background_down: 'icons/camera_red.png'
"""
HS0 = """
<HomeScreen0>:
AnchorLayout:
Button:
size_hint: None, None
size: dp(200), dp(48)
on_release: root.manager.current = root.manager.next()
text: 'Next'
"""
class HomeScreen0(Screen):
def __init__(self, **kwargs):
Builder.load_string(HS0)
super().__init__(**kwargs)
class ButtonsLayout1
(RelativeLayout):
screen_manager = ObjectProperty() # this is set in kv, where the ButtonLayout1 is instanced
def __init__(self, **args):
Builder.load_string(BL1)
super().__init__(**args)
def go_home(self):
# go to the top of the widget tree (app.root), app.root is the screen manager
app = App.get_running_app()
app.root.current = '0'
print(f'app.root: {app.root}')
# or you can access the screen manager as:
print(f'Screen manager using property: {self.screen_manager}')
# or "manually" walk up the widget tree - yuck!
print(f'Use the parent attributes: {self.parent.parent.parent}')
.sm
MyApp().run()1. Use a partial, https://docs.python.org/3/library/functools.html#functools.partial
2. Use a lambda – A lambda is an anonymous function. This is effectively the same as creating a stub, but more convenient.
3. Create a stub function, so you call a function with no parameters, and from that actually make the call you want
4. Use kv – this is my preferred option.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/CAAT1hNAYr_Ny4fDNvjGGLv6PvfmZ0GCuMi-1bY5gZ%2B9uODRxkw%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/CAAT1hNB47FpbTzypsxC9rfhdB0WqMXsm19UYEtLH920G3-Dz3A%40mail.gmail.com.
_ is used to indicate that the input variable is a throwaway variable/parameter and thus might be required or expected, but will not be used in the code following it.To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/87733AA9-A32D-44FF-BE80-6BD5D4C5E05D%40cox.net.
On Mar 7, 2023, at 4:19 PM, Gary Kuipers <gary.k...@casinfosystems.com> wrote:
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/CAAT1hNC4K0nnbhqHE8svCb-2V_T4fT37fbVaXUAkf6fpepWtAA%40mail.gmail.com.