Instantiation of Kivy Children Widgets between Python and Kivy Language - python

I am having issues getting Kivy to instantiate children Widgets. The issue here I believe is with how I am setting up the root widget. According to many examples I have seen including kivy's own documentation I should be able to instantiate children widgets to a root widget in the .kv file without using <> as such:
initApp.kv root_rule:
Root_Widget:
Test_Screen:
<Test_Screen>:
BoxLayout:
*there is stuff here, leaving blank to keep this question shorter*
Python File: (Note that the add_widget() is commented out)
class Test_Screen(Screen):
pass
class Root_Widget(ScreenManager):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# self.add_widget(Test_Screen())
pass
class InitApp(App):
def build(self):
return Root_Widget()
if __name__ == '__main__':
InitApp().run()
However, this only leads to a blank app. There are two ways to fix this:
Uncomment the self.add_widget(Test_Screen()) line OR
Add <> to around the Root_Widget in the .kv file so it would then be a class rule:
initApp.kv class_rule:
<Root_Widget>:
Test_Screen:
<Test_Screen>:
BoxLayout:
*there is stuff here, leaving blank to keep this question shorter*
Question
My question is, what is the difference here? Nesting Test_Screen underneath Root_Widget in the .kv file should be the exact same as calling the add_widget() method correct? Is this true and if so how/why is it different when setting Root_Widget as a class rule VS. a root rule?
Are there unforeseen consequences to saying that Root_Widget is a class rule rather than a root rule? It seems to work just fine using this approach but I don't know if it will cause other trouble as the app becomes more complex.

Blank Window/App
The blank window/app could be due to one of the following:
kv file name mismatch e.g. app class name, class InitApp(App): then kv filename should be init.kv. If you are not using Builder to load kv then the kv file name should be init.kv and not InitApp.kv
No root widget in the return statement
No root rule in the kv file
kv filename - By name convention
By name convention
Kivy looks for a Kv file with the same name as your App class in
lowercase, minus “App” if it ends with ‘App’ e.g:
MyApp -> my.kv
Solution - Blank Window/App
Instantiate and add a Label widget under Test_Screen as follow:
<Test_Screen>:
BoxLayout:
Label:
text: "kv file - Root Rule vs Class Rule"
Question 1
what is the difference here? Nesting Test_Screen underneath
Root_Widget in the kv file should be the exact same as calling the
add_widget() method correct?
Answer 1
Yes it is the same.
Kivy App - without kv file
Use add_widget(Test_Screen()) function to instantiate add children widgets under the parent.
Kivy App - with kv file
Instantiate and add children widgets under the parent using Test_Screen:
Can use both e.g. using add_widget(Button()) to dynamically instantiate and add children widgets to a ScrollView.
Question 2
Is this true and if so how/why is it different when setting
Root_Widget as a class rule VS. a root rule?
Answer 2
When the kv file is parsed, the root rule will be set as the root attribute of the App instance. Whereas a class rule, defines how any instance of that class will be graphically represented.
Question 3
Are there unforeseen consequences to saying that Root_Widget is a class rule rather than a root rule?
Answer 3
I don't believe there is any unforeseen consequences. The only thing to note when using root rule or class rule in kv file are as follow.
kv file - Using root rule
If in your kv file, you have a root rule defined i.e. Root_Widget: then in your Python code, you can do one of the following:
Option 1
The kv file is init.kv
class InitApp(App):
pass
Option 2
The kv file is not init.kv but InitApp.kv
class InitApp(App):
def build(self):
return Builder.load_file("InitApp.kv")
kv file - Using class rule
If in your kv file, you have a class rule defined i.e. <Root_Widget>: then in your Python code, you can do one of the following:
Option 1
The kv file is init.kv
class InitApp(App):
def build(self):
return Root_Widget()
Option 2
The kv file is not init.kv but InitApp.kv
class InitApp(App):
def build(self):
Builder.load_file("InitApp.kv")
return Root_Widget()

Related

usage of super() in class

I've wrote this code but I don't understand a part of it; the line that uses super(check_box, self).__init__(**kwargs) and when I remove check_box, self from it, the code still functions properly
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.checkbox import CheckBox
from kivy.uix.gridlayout import GridLayout
class check_box(GridLayout):
# for class name don't use keywords like CheckBox or if you use them you should use underscore in the name as well
def __init__(self, **kwargs):
# **kwargs imports keywords related to gridlayout class
super().__init__(**kwargs)
self.cols = 2
# number of columns
self.add_widget(Label(text="Male"))
self.active = CheckBox(active=True)
# making an object
self.add_widget(self.active)
# adding the object to the GUI
self.add_widget(Label(text="Female"))
self.active = CheckBox(active=True)
self.add_widget(self.active)
self.add_widget(Label(text="Other"))
self.active = CheckBox(active=True)
self.add_widget(self.active)
class CheckBoxApp(App):
def build(self):
return check_box()
if __name__ == "__main__":
CheckBoxApp().run()
check_box class inherits the GridLayout class. Calling super() inside check_box class allows us to make a proxy object (temporary object of the superclass) that allows us to access methods of the superclass class.
super().init(param1, param2, ...) simply means that we are calling the constructor method of that temporary superclass object to construct that object by feeding in parameter values.
Arguments inside the super are not used now, but they anyway work without error because they were used in old python versions.

Kivy - dynamically alter base widget property

I would like to change a base widget property in kivy. This is easily done in the kivy language like so:
<Label>:
font_size: sp(30)
This does not have to be in my widget tree to affect all classes that use Label as a base class upon app initialization. So, my following example app shows up with two buttons whose text is larger than the default, which is what I want.
testApp.py
class Main_Screen(Screen):
pass
class Root_Widget(ScreenManager):
pass
class testApp(App):
def build(self):
return Root_Widget()
if __name__ == '__main__':
testApp().run()
test.kv
<Root_Widget>:
Main_Screen:
BoxLayout:
Label:
text: 'Label1'
Label:
text: 'Label2'
<Label>:
font_size: sp(30)
However, I would like to update this font_size value based on my window size (which I know how to change dynamically I just haven't included all that here). But how do I access the font_size property of the base class widget from python so that it can be changed?
Questions
How can I accomplish this change via python in a way that allows me to change it at the instantiation of my app?
Add import statement for label, from kivy.uix.label import Label
Add import statement for sp, from kivy.metrics import sp
Use Label.font_size = sp(30)
Snippet - Python code
from kivy.uix.label import Label
from kivy.metrics import sp
class testApp(App):
def build(self):
Label.font_size = sp(30)
return Root_Widget()
Output

trouble with scope related variables

Hello fellow programmers, I'm developing a simple interface application in Python that will enable easy and intuitive entry of inventory forms in an access database.
I currently have a function as follows:
def spawnerror(self, errormsg):
self.running = False
content = Button(text=errormsg)
popup = Popup(title='ERROR!', content=content, auto_dismiss=False)
content.bind(on_press=popup.dismiss)
popup.open()
And I have appropriate error handling done, and the application uses this function as intended. For example, if someone doesn't enter in a required field, it calls this function and spawns an error page with an error and informs the user.
My issue that I run into is that, it needs to set the class variable running to False, because at the end of the main function "submit" it checks for that and if self.running == False, then it needs to skip the execution of data entry in the access database.
Why is this function not setting the class variable of running to false?
Solution - using App.get_running_app()
In the example, a class attribute, running is defined as BooleanProperty. In the spawnerror() function, it uses App.get_running_app() function to get an instance of App class and then access variable, running.
Note
If running, spawnerror() function and submit() function are in different classes then work out the relation of the classes and pass a direct reference between them.
Example
main.py
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import BooleanProperty, ObjectProperty
class RootWidget(BoxLayout):
instance = ObjectProperty(None)
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
self.instance = App.get_running_app()
self.spawnerror('Testing')
def spawnerror(self, errormsg):
self.instance.running = False
content = Button(text=errormsg)
popup = Popup(title='ERROR!', content=content, auto_dismiss=False)
content.bind(on_press=popup.dismiss)
popup.open()
class TestApp(App):
running = BooleanProperty(True)
def build(self):
print("\nbuild:")
self.display_attributes()
return RootWidget()
def on_stop(self):
print("\non_stop:")
self.display_attributes()
def display_attributes(self):
print("\tApp.running =", self.running)
if __name__ == "__main__":
TestApp().run()
Output

Python / Kivy Initial Variable Assignment

I'm relatively new to Python and OOP, and going through the Kivy documentation and found some syntax I was not used to seeing. I am not really sure how to search for the answer since I don't know what to call this type of syntax, so I'll illustrate with the code itself.
Here is the code from the kivy docs:
import kivy
kivy.require('1.0.5')
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()
When the above code runs
return Controller(info='Hello world')
I understand that it is setting a value for Controller().info (or controller_object_instance.info within the kivy.app.build() code), but I don't understand how this string assignment to the variable 'info' works, since I would think you would need an init() function at the top of the class to accept this assignment. Since there is no init() within the Controller() class, why doesn't
Controller(info='Hello world')
throw an error? How does the above line of code know to properly assign the value 'Hello world' to its own attribute 'info'? Is there some init() function inherited from App and this can someone be used or does it have something to do with these two lines of code?
label_wid = ObjectProperty()
info = StringProperty()
Any help appreciated. Just looking for a way to search for this more on my own, and I'm stuck on what topic to search for.
When you created the class Controller, you wanted to inherit the class FloatLayout.
This means, whenever you create a Controller object, you create a FloatLayout which you extended. So the __init__ function from FloatLayout at executes.
Look at this example:
class Animal:
def __init__(self,**kwargs):
self.info = kwargs.get("info")
class Dog(Animal):
pass
obj = Dog(info="Hello world")
print(obj.info)
Now you want to create a Dog, but a dog is also an Animal, so you want to inherit all it's methods and attributes.
So if you pass info as an argument when creating an instance of Dog, it will be passed to Animal's __init__ function.

Kivy run function from kv button

I'm new at kivy and try to run a do_login function inside my MyApp Class from a kv generated button.
my kv layout with the button
RelativeLayout:
FloatingActionButton:
id: float_act_btn
on_press: ???how to call call do_login from MyApp
and my class with the containing do_login function
class MyApp(App):
def build(self):
main_widget = Builder.load_string(login_kv)
def do_login(self, *args):
print'jo'
How to use on_press to call do_login?
with on_press:do_login(login.text, password.text)' I get 'do_login' is not defined and the same with self.do_login I get MaterialRaisedButton' object has no attribute 'do_login'
make do_login a member of the MyApp class:
class MyApp(App):
def build(self):
main_widget = Builder.load_string(login_kv)
def do_login(self, *args):
print'jo'
and use app in kv as the keyword to access MyApp and call the function:
on_press: app.do_login()
from Kivy language:
There are three keywords specific to Kv language:
app: always refers to the instance of your application.
root: refers to the base widget/template in the current rule
self: always refer to the current widget

Categories

Resources