Kivy: how to instantiate a dynamic classes in python - python

I'm having a hard time trying to figure out how to instantiate a dynamic class I created using kv lang on my python code, consider the following code:
My test.kv file looks like this:
<MyPopup#Popup>:
title:'hello'
size_hint:(1, .6)
GridLayout:
id:root_grid
cols:2
padding:['8dp', '4dp','8dp','4dp']
spacing:'8dp'
Label:
text:'some text here'
Button:
text:'Ok'
on_press:do_something()
<MyGrid>:
rows:1
Button:
text:'Show Popup'
on_press:root.pop.show()
Then in my test.py:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.popup import Popup
from kivy.factory import Factory
class MyGrid(GridLayout):
pop = Factory.MyPopup()
pass
class Test(App):
def build(self):
return MyGrid()
if __name__=='__main__':
Test().run()
The I get the following error: raise FactoryException('Unkown class <%s>' % name) kivy.factory.FactoryException: Unkown class
Can somebody please explain me how to properly do it, what am I missing? If you need any more information please let me know. Thanks.

Your call to the Factory takes place before the kv file is loaded, therefore the class you want does not yet exist.
Unless there is some reason to need a class level attribute, set self.pop in the __init__ of MyGrid instead.
You could also just include a Python class declaration. I generally prefer to do this for anything that I want to interact with from python, though opinions vary.

Related

Updating Kivy Widgets when Root Object Changes

I have a data structure in the form of an object that I am representing graphically using Kivy.
If possible, I would like to write it so that when the root object is changed, the widgets in Kivy reflect the change. So far what I've noticed is that I can call on the object from the KV language when the widgets are initially created, and I can have those widgets modify the root object, but
widgets that should be "bound" to the root object variables do not get updated.
I'm looking to find a way to "bind" them so that I don't have to write a "refresh/reload" function that loops through my data object each time a change is made.
Below is sample code that shows how I have my code set up so far.
From what I understood in the kivy api docs, using ObjectProperty(object, rebind=True) should be doing what I am trying to have done.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
class DataObject():
def __init__(self, name="foo", data="bar"):
self.name = name
self.data = data
class MainWindow(Screen):
pass
class WindowManager(ScreenManager):
data = DataObject()
data_obj = ObjectProperty(data, rebind=True)
kv = Builder.load_file("my.kv")
class MyMainApp(App):
def build(self):
return kv
if __name__ == '__main__':
MyMainApp().run()
WindowManager:
MainWindow:
<MainWindow>:
name: "Main"
GridLayout:
cols: 1
GridLayout:
cols: 2
Label:
text: root.manager.data_obj.name
Button:
text: "Change Name"
on_release:
root.manager.data_obj.name = "Bar"
print(root.manager.data_obj.name)
Pressing the Button "Change Name" changes the object data_obj.name from "foo" to "bar" and prints that to the console confirming it changed
I would expect the Label text to also change to "bar"
rebind works for Properties, and not for class attributes so your logic fails. One possible solution is that DataObject is an EventDispatcher and name, data is ObjectProperty:
from kivy.event import EventDispatcher
class DataObject(EventDispatcher):
name = ObjectProperty("foo")
data = ObjectProperty("bar")

How to Bind on_press to GridLayout in kivy python

i was trying to bind on_press method to gridLayout in kv language but i got this error AttributeError: pressed. I did some research even in the kivy doc but no help .So if any one has a solution to this problem please you may be a good resource
here is my testApp.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class screendb(BoxLayout):
def mycall_back(self):
print('hello')
class testApp(App):
def build(self):
return screendb()
if __name__=='__main__':
testApp().run()
here is my testApp.kv
<Screendb#BoxLayout>:
GridLayout:
on_pressed:root.Mycall_back()
In your py file:
# Behaviors let you add behavior from one widget to another widget
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.gridlayout import GridLayout
# We create a new widget that is a GridLayout with access to Button Behaviors
# Like Button's 'on_press' method
class ButtonGrid(ButtonBehavior, GridLayout):
pass
class screendb(BoxLayout):
def mycall_back(self):
print('hello')
class testApp(App):
def build(self):
return screendb()
In your kv file:
# We create a Root Widget for the ButtonGrid
<ButtonGrid>:
<Screendb#BoxLayout>:
# We add an instance of the ButtonGrid class to our main layout
ButtonGrid:
# We can now use on_press because it is a part of the ButtonBehavior class, that makes up your ButtonGrid class.
on_press:root.mycall_back()
Note: There were a few minor mistakes in your post as well. For example, there is no method 'on_pressed', only 'on_press', you also wrote your callback as 'mycall_back' in your py file while writing it as 'Mycall_back' in your kv file, which refers to a different method that exists. Make sure your letter cases match.
video example

Kivy Python - Button to increment variable, without using NumericProperty

I'm a complete novice to both python and kivy having learnt python from codeacademy about a week ago and kivy from youtube tutorials.
Could someone please explain to me why the code below does not result in a screen with a label displaying n, which is incremented by the button?
Python file
import kivy
from kivy.app import App
from kivy.uix.button import Button, Label
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import NumericProperty
class Example(BoxLayout):
n = 0
def n_plus(self):
self.n += 1
class ExampleApp(App):
def build(self):
return Example()
example = ExampleApp()
example.run()
KV file
<Example>:
BoxLayout:
Label:
text: str(root.n)
Button:
text: "+1"
on_press: root.n_plus()
Then could you explain why making n = NumericProperty(0) makes this work?
I'd like to run some functions on n which don't seem to work on numeric properties.
Because when you use NumericProperty()
As the official document said:
It produces events such that when an attribute of your object changes,
all properties that reference that attribute are automatically
updated.
So, in short, it creates a binding relationship between your UI(.kv) and attribute of its class(.py)
But, actually, you can modify the UI by yourself without the help from the kivy framework. I changed your example as the following:
Add an id attribute to your widget
Access the id attribute by using self.ids.your_id_in_kv_file
But it's obviously not good, since now you need to update your UIby yourself everytime you want to update your UI. But with that XXXXProperty from kivy, you just need to change the value of that attribute, you don't need to worry about the UI at all.
Another disadvantage of the solution is that, when you need to change the UI, you need to change tons of code if you modify them all by yourself...
Here is the example:
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.lang.builder import Builder
from kivy.uix.boxlayout import BoxLayout
Builder.load_string("""
<Example>:
BoxLayout:
Label:
id: lbl
text: "0"
Button:
text: "+1"
on_press: root.n_plus()
""")
class Example(BoxLayout):
def n_plus(self):
value = self.ids.lbl.text
self.ids.lbl.text = str(int(value) + 1)
class ExampleApp(App):
def build(self):
return Example()
if __name__ == '__main__':
ExampleApp().run()

How to pass arguments to build() in kivy?

I have written a code with GridLayout which need to be added with buttons in Python file. So, the add_widget() mainpulation should be done in build(). I am getting errors and couldn't get it.Someone Please help me.
In short, instead of add_btn(), I need it in build() of MineApp class.
Thanks in advance.`
main.py
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
class MainLayout(BoxLayout):
def build(self):
pass
def add_btn(self,id):
for i in range(100):
id.add_widget(Button())
class MineApp(App):
def build(self):
return MainLayout()
if __name__ == '__main__':
MineApp().run()
mine.kv file:
<MainLayout>:
orientation:'vertical'
BoxLayout:
orientation:'horizontal'
height: '30px'
size_hint_y:None
TextInput:
id: tinput
text:'10'
Button:
text:'start'
on_press:root.add_btn(grid)
Label:
id:mylabel
text:'0'
GridLayout:
id: grid
cols:10
rows:10
It took some time but think I get what you're trying to say! You can get the same effect by passing the id parameter of Grid in the .py file like so...
class MainLayout(BoxLayout):
def build(self):
for i in range(100):
self.ids.grid.add_widget(Button())
Then, you can just take your start button and...
Button:
text:'start'
on_press:root.build()
This works because "self" in .py refers to the class while in the .kv a similar wording would be 'root' (while 'self' in .kv refers to the widget!) Is this what you kind of had in mind? Let me know! I tried it out myself and had no problem running it through :)
Also, it wasn't put explicitly here that to test the code you'd need to import Builder via:
from kivy.lang import Builder
and do...
Builder.load_file("mine.kv")
as your .py and .kv don't share the same name (which you'd still need to add a "#File name: main.py" to the .kv if they did match names! Other than that, it looks good!

What's the idea behind ObjectProperty in this example from the Kivy docs?

I really try to get the concept of Kivy. I'm aware it's powerful and useful, but for me at some points also hard to understand, at least by the given examples in here. In the section "Designing with the Kivy Language" I found following example:
Python-file
from kivy.uix.floatlayout import FloatLayout
from kivy.app import App
from kivy.properties import ObjectProperty, StringProperty
class Controller(FloatLayout):
''' Create a controller that receives a custom widget from the kv lang file.
Add an action to be called from the kv lang file. '''
# label_wid = ObjectProperty()
info = StringProperty()
def do_action(self):
self.label_wid.text = 'My label after button press'
self.info = 'New info text'
class ControllerApp(App):
def build(self):
return Controller(info='Hello world')
if __name__ == '__main__':
ControllerApp().run()
controller.kv
<Controller>:
label_wid: my_custom_label
BoxLayout:
orientation: 'vertical'
padding: 20
Button:
text: 'My controller info is: ' + root.info
on_press: root.do_action()
Label:
id: my_custom_label
text: 'My label before button press'
There is some little deviation from the original: I made the line # label_wid = ObjectProperty() a comment, trying out to get the idea behind it, expecting the code not to run. But it is running! So would someone with a better understanding of Kivy be so kind to explain, why this line of code is useful (maybe at least for educational reasons), if not yet required (by any circumstance, of which I'm not aware of)?
When you add a kv line for a property that doesn't exist, it is automatically created, so the kv is actually the same in this case as putting label_wid = ObjectProperty() in the python. However, adding the property in Python is more explicit (especially if you will access it from python) and lets you be certain that a property of the right type will be created.

Categories