Python / Kivy Initial Variable Assignment - python

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.

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.

Howto access an already initialized class variable in main.py from another class

I have a class with a set of variables that is initialized and accessed from my main.py script. I have another class that is imported from a folder one level deep from the main.py file.
I can access the already set variables anywhere in my main.py file by referencing appVars.some_variable. However, I am not able to access them in the --screen1.py class. It seems the --Variables.py class is being reinitialized in the nested class which results in a blank value.
How do I access the already initialized class variable from the main.py script inside of my --ScreenOne.py script?
File/Folder Structure
main.py
-classes
--ScreenOne.py
--Variables.py
variables.py
from kivy.properties import StringProperty
class Variables():
def __init__(self):
self.server_time = StringProperty('')
main.py
from classes.ScreenOne import ScreenOne
from classes.Variables import Variables
appVars = Variables()
class SomeApp(App):
def update_clock(self, *args):
appVars.server_time = datetime.now()
--ScreenOne.py
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty
from classes.Variables import Variables
class ScreenOne(Screen):
def on_enter(self):
print(Variables.server_time)
I have also tried adding appVars = Variables() to the beginning of the --ScreenOne.py script and referencing appVars.server_time however, it did not work as well.
In main.py you initiate class Variables by appVars = Variables(). But in ScreenOne you import Variables but never initiate it, besides initiating it will give a new instance not the same as appVars in main (also print(Variables.server_time) will give an error).
To use appVars in ScreenOne you will need to pass appVars to this class, like:
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty
class ScreenOne(Screen):
def __init__(self, appVars):
self.appVars = appVars
def on_enter(self):
print(self.appVars.server_time)
Then when you initiate ScreenOne you do something like:
screen_one = ScreenOne(appVars)
Note in ScreenOne, you do not need to import Variables as all attributes of this class will come with appVars.
By the way as per PEP8 variable names should not use capitals but underscore instead, appVars -> app_vars.

How to access classes using kivy?

How to access a class in python code using on_release: by kivy? For example, a function that is in root I use. root.function() and a class?
on_release: # What to put here to access the function `chama`?
class Tela(ScreenManager):
pass
class teste(Screen):
def chama(self):
pass
def save_d(self):
class Prg(App):
def build(self):
return Tela()
Prg().run()
If you use kv (file or string), then a good way to access other classes is to use ids.
Here is an example:
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
class Tela(ScreenManager):
pass
class Teste(Screen):
def chama(self):
print ('Hello')
class Teste2(Screen):
pass
class PrgApp(App):
def build(self):
return Builder.load_file('Prg.kv')
PrgApp().run()
Prg.kv:
Tela
Teste2
Button
on_release: t.chama()
Teste
id: t
note: the first letter of a widget class must be capitalized, otherwise you will get a syntax error

Instantiation of Kivy Children Widgets between Python and Kivy Language

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

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

Categories

Resources