I cannot for the life of me figure out how to pass a custom property on a custom widget via the KV file. My application is a simple grid that contains a Button() and TestWidget(). TestWidget() has a StringProperty() test_property that doesn't seem to get the data from the KV file as seen by the print statement on init. Here's some quick straight forward code as an example.
Thanks.
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.properties import StringProperty
Builder.load_string("""
<TestWidget>:
<TestGrid>:
Button:
TestWidget:
test_property: 'Test Property'
""")
class TestWidget(Widget):
test_property = StringProperty()
def __init__(self, **kwargs):
super(TestWidget, self).__init__(**kwargs)
print('Test OUTPUT:', self.test_property)
class TestGrid(GridLayout):
pass
class MyApp(App):
def build(self):
return TestGrid()
MyApp().run()
I think I figured it out. Kivy doesn't pass anything to the objects. I learned this at https://kivy.org/docs/api-kivy.properties.html.
I use the on_ to do what needs to be done. There is a big difference between Kivy Objects and Python Objects.
Here's an example of a custom BoxLayout;
class KivyInput(BoxLayout):
text_test = StringProperty()
def __init__(self, **kwargs):
super(KivyInput, self).__init__(**kwargs)
self.orientation = 'horizontal'
self.label = Label()
self.text_input = TextInput(multiline=False)
self.add_widget(self.label)
self.add_widget(self.text_input)
def on_text_test(self, instance, value):
self.label.text = value
def remove(self):
self.clear_widgets()
Try printing it on the upcoming frame, instead of in the initiation of the object.
After the object is created, you can access the properties.
You do that with Clock.
Like this:
from kivy.clock import Clock
class TestWidget(Widget):
test_property = StringProperty()
def __init__(self, **kwargs):
super(TestWidget, self).__init__(**kwargs)
Clock.schedule_once(self.after_init) # run method on next frame
def after_init(self,dt):
print('Test OUTPUT:', self.test_property)
Related
I'm learning to program with Python and Kivy.
I'm trying to change the text of a Label inserted in one class, by pressing the button which is in another
class. Without using Kv language
I tried them all, the text does not change. Help me!!!!
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
class Gestione(BoxLayout):
def __init__(self, **kwargs):
super(Gestione, self).__init__(**kwargs)
self.w1 = Windows()
self.w2 = Windows2()
self.add_widget(self.w1)
self.add_widget(self.w2)
class Windows(BoxLayout):
def __init__(self, **kwargs):
super(Windows,self).__init__(**kwargs)
self.label = Label()
self.label.text = "Label to change in Window1"
self.add_widget(self.label)
class Windows2(BoxLayout):
def __init__(self, **kwargs):
super(Windows2, self).__init__(**kwargs)
self.link = Windows()
bottone = Button(text="button Class2", on_press=self.changetext)
self.add_widget(bottone)
def changetext(self, *args, **kwargs):
self.link.label.text = "Text changed"
class Gestioneapp(App):
def build(self):
return Gestione()
Gestioneapp().run()
Please help, I'm trying to make an application for a child to learn the alphabet, I'm just learning programming and working with classes.
I need to pass to the SecondScreen class the text that the button_press function returns from the ScreenMain class.
`
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from itertools import chain
from kivy.uix.screenmanager import ScreenManager, Screen
class ScreenMain(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.alphabet = [chr(i) for i in chain(range(1040, 1046), range(1025, 1026), range(1046, 1069), range(32, 33),
range(1069, 1072))]
gr = GridLayout(cols=5, padding=[35], spacing=3)
for i in self.alphabet:
gr.add_widget(Button(text=i, on_press=self.button_press))
self.add_widget(gr)
def button_press(self, instance):
self.manager.transition.direction = 'left'
self.manager.current = 'second_screen'
print(instance.text) # I output to the console, everything is ok
return instance.text
class SecondScreen(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
get = ScreenMain.button_press # I'm trying to pass a letter from a function to a class button_press
layout = GridLayout(cols=5, rows=5, padding=[35], spacing=10)
layout.add_widget(Button(
text=str(get))) # Trying to create a button on the second page with the text of the letter pressed on the first screen
self.add_widget(layout)
def _on_press_button_new_layout(self, *args):
self.manager.transition.direction = 'right'
self.manager.current = 'main_screen'
class MyApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(ScreenMain(name='main_screen'))
sm.add_widget(SecondScreen(name='second_screen'))
return sm
if __name__ == '__main__':
MyApp().run()
`
This sample code
from kivy.app import App
from kivy.properties import AliasProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class SampleLayout(BoxLayout):
def get_something(self):
return self.sample_widget.width + 30
something = AliasProperty(get_something, bind=['sample_widget.width'], cache=True)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.sample_widget = Button(on_release=self.sample_test)
self.add_widget(self.sample_widget)
def sample_test(self, *args):
print(self.something)
class MyApp(App):
def build(self):
return SampleLayout()
MyApp().run()
Returns this error
AttributeError: type object 'SampleLayout' has no attribute 'sample_widget.width'
bcs (I guess) bind is searching for an attribute of the instance and not an attribute of an attribute of the instance... Maybe with getattr() which also doesn't work if using dots in the variable name.
So, is there an API for such bind? or should I bind directly with self.sample_widget.bind(width=idontknow)?
If it is the last case, what is that function idontknow to call the getter of a (cached) readonly property?
Understand also that the code above is an example, I need this functionality for something more complex. I cannot just put print(self.sample_widget.width + 30) under sample_test to get it working.
Thanks a lot.
Answering myself :)
I don't know if this is the better approach, but works.
sample_width = NumericProperty() # an intermediate
...
something = AliasProperty(get_something, bind=['sample_width'], cache=True)
...
# sync the intermediate with the wanted var
self.sample_widget.bind(width=self.setter('sample_width'))
so the example code would be like this:
from kivy.app import App
from kivy.properties import AliasProperty, NumericProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class SampleLayout(BoxLayout):
def get_something(self):
return self.sample_widget.width + 30
sample_width = NumericProperty() # an intermediate
something = AliasProperty(get_something, bind=['sample_width'], cache=True)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.sample_widget = Button(on_release=self.sample_test)
self.add_widget(self.sample_widget)
# sync the intermediate with the wanted var
self.sample_widget.bind(width=self.setter('sample_width'))
def sample_test(self, *args):
print(self.something)
class MyApp(App):
def build(self):
return SampleLayout()
MyApp().run()
I have created a group of images in a for loop in kivy and those images have the attribute of 'buttonbehavior' making them clickable. I am trying to change the image at a particular index to another image when clicked but it is not working.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.image import Image
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
class ScreenManagement(ScreenManager):
def __init__(self, **kwargs):
super(ScreenManagement, self).__init__(**kwargs)
class Screen2(Screen):
def __init__(self, **kwargs):
super(Screen2, self).__init__(**kwargs)
Pass
class ImageButton(ButtonBehavior,Image):
pass
class Screen1(Screen, ButtonBehavior,Image):
def __init__(self, **kwargs):
super(Screen1,self).__init__(**kwargs)
for i in range(6):
self.btn= ImageButton(source='put_any.png', size_hint=(.3, .2),
pos_hint={'center_x': .5, 'center_y': .32})
self.add_widget(self.btn)
If i==0:
self.btn.bind(on_press=self.change_image)
def change_image(self,*args)
self.source='put_any.png'
class Application(App):
def build(self):
sm = ScreenManagement(transition=FadeTransition())
sm.add_widget(Screen1(name='screen1'))
sm.add_widget(Screen2(name='screen2'))
return sm
if __name__ == "__main__":
Application().run()
I want the image at index 0 in the for loop to change to another image when it is pressed, but it is not working please what am I doing wrong.
If you refer self.source it means Screen.source that wont affect anything.
Change the func like this:
def change_image(self, btn)
btn.source='put_any.png'
I am trying to bind a widgets' property to that of its child. I have used the "setter", as pointed out by the documentation. The parent's property is however never changed and its value remains as in the beginning (None).
Any help is appreciated!
Here is my code example:
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.checkbox import CheckBox
from kivy.uix.label import Label
MESSAGE = "Checkbox active? '{}'"
class MainScreen(BoxLayout):
child_state = ObjectProperty()
def __init__(self, *args, **kwargs):
super().__init__(orientation="vertical", *args, **kwargs)
self.checkbox = CheckBox()
# Why doesn't this work?
self.bind(child_state=self.checkbox.setter("active"))
self.label = Label(text=MESSAGE.format(self.child_state))
self.button = Button(text="Print properties",
on_release=self.print_properties)
self.add_widget(self.checkbox)
self.add_widget(self.label)
self.add_widget(self.button)
def on_child_state(self, *args):
self.label.text = MESSAGE.format(self.child_state) # Never updated
def print_properties(self, *args):
print(f"child_state property {self.child_state}") # Always None
print(f"checkbox property {self.checkbox.active}") # Changes between True/False
print()
class MyApp(App):
def build(self):
return MainScreen()
if __name__ == '__main__':
MyApp().run()