Kivy : load_string() vs kv file - python

In order to understand the logic behind Kivy kv language, I'm trying to rewrite a minimal application by replacing the automatic load of a kv file by a call to Builder.load_string().
Here's my starting point (source: examples 1-2, 1-3): two files, weather.py and weather.kv :
weather.py:
from kivy.app import App
class WeatherApp(App):
pass
if __name__ == '__main__':
WeatherApp().run()
and weather.kv:
Label:
text: "Hello World"
Up to there, everything's alright
.
But if I try to load manually the kv stuff, I just get a black screen (and no error message). My code:
from kivy.app import App
from kivy.lang import Builder
KV = '''
Label
text: "Hello World"
'''
Builder.load_string(KV)
class WeatherApp(App):
pass
if __name__ == '__main__':
WeatherApp().run()
I'm obviously missing something here, but what ? Any help would be appreciated !

When you create a .kv there are basic but strict rules, among them there can only be one toplevel, the toplevel is identified because it does not have "<>", besides for the App to recognize it it must have the same name of the application in lowercase, in your case the .kv is called weather.kv and the WeatherApp app. But the above does not happen if you use Builder, in the case that the .kv has a root as it is in your case Builder.load_string() returns it so you must return it in the build method of the App:
from kivy.app import App
from kivy.lang import Builder
KV = '''
Label:
text: "Hello World"
'''
root = Builder.load_string(KV)
class WeatherApp(App):
def build(self):
return root
if __name__ == '__main__':
WeatherApp().run()

Related

Kivy Issues with .py and .kv

I'm using Windows 10, Python 3.7.9
My code:
In Main.py:
from kivy.app import App
from kivy.uix.widget import Widget
class MyGrid(Widget):
pass
class Main(Widget):
def build(self):
return Main()
if __name__ == "__main__":
Main().run()
In main.kv:
<Main>
GridLayout:
cols:1
GidLayout:
cols:2
Label:
text: "Name: "
TextInput:
multiline:False
Label:
texxt: "Email: "
TextInput:
multiline:False
Button:
text:"Submit"
Error(1):
In the .py file, 'Main' in 'Main().run()' is underlined with:
Instance of 'Main' has no 'run' member
Error(2):
In the .kv file '' is underlined with:
Kivy files require #:Kivy
I can't figure out how to fix, any help is appreciated
I see two bugs, one in the kv code, and the second in the python code.
In the kv code change the <Main> to <MyGrid>.
MyGrid class is the root widget, not the Main class. The Main class is only responsible to build the widget tree.
In the python code, change the return of the build method in the Main class so that it returns a MyGrid instance. And the Main should inherit from the App class, not from the Widget class.
class Main(App):
def build(self):
return MyGrid()
According to the documentation, a kv file:
Syntax of a kv File A Kivy language file must have .kv as filename
extension.
The content of the file should always start with the Kivy header,
where version must be replaced with the Kivy language version you’re
using. For now, use 1.0:
#:kivy `1.0`
# content here
Then, in your py file. an application must extend App not Widget. Also, the build() method must return a Widget, not an instance of the App. And, if your App is named Main, then you should choose a different name for the root widget of your App.
The rules in the kv file describe how to build widgets, and cannot be applied to building an App. They can describe the building of the root widget of the App, but not the App itself.

Why does a syntax error show up near <MyGrid> when I run my kv file?

I just started trying out kivy developing and ran into a problem. When I try to run a simple kivy file with a corresponding python file, it emits a syntax error. does anyone know why this happened? FYI I'm using Atom IDE with python 3.8.3, when I ran kivy based on python, it worked flawlessly, but when I try to run a rewritten kv code, it won't run.
Here's the python source code:
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.widget import Widget
class MyGrid(Widget):
pass
class MyApp(App):
def build(self):
return MyGrid()
if __name__ == "__main__":
MyApp().run()
...and the kv code:
< MyGrid >
Label:
text: "example text"
Error details:
File "C:\Users\ljhub\Code Storage\Python Projects\my.kv", line 1
<MyGrid>
^
SyntaxError: invalid syntax
The corrected kv file should be as below:
<MyGrid>:
Label:
text: "example text"
You have to add a colon after any declaration in kv language.
such as:
<MyGrid>:
or
Label:

Create widgets based on user input, in the .kv file

I want to ask the user for a number, then display that amount of widgets.
This is how I do it:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
class MainApp(App):
def build(self):
return List()
class List(GridLayout):
def __init__(self, **kwargs):
super(List, self).__init__(**kwargs)
self.cols = 1
user_input = 3 # Just an example
for i in range(user_input):
label = Label(text="Widget number {}:".format(i + 1))
self.add_widget(label)
if __name__ == "__main__":
app = MainApp()
app.run()
The problem is: the point of "kivy language" is to keep the logic in the .py file, and the design in the .kv file.
Is there any way I can keep the design only in .kv file, while doing this (having a for loop)?
(I'm new to kivy, sorry if I'm asking a simple question. :) )
Definitely, you can.
The KV file is used primarily to design the looks while the login part of a program is handled by the PY file.
.kv file
<classname>:
Button:
text:'click me'
on_release: root.function_to_call_from_py_file()
.py file
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
class List(GridLayout):
def function_to_call_from_py_file(self):
print('This function is called')
class MainApp(App):
def build(self):
return List()
if __name__ == "__main__":
app = MainApp()
app.run()

Is it possible to have a folder-based structure in kivy?

I am trying to make an app. I have got it to work where all my files are in the same folder but it gets very messy and I would like to separate my files into separate folders of the structure: Start, Prelogin, andFirebaseLoginScreen`. So is what I am trying to do possible in kivy?
I have tried #: import name x.y.z as the pointer where x is the folder name, y is the name of kv-file and z is the class in kv-file I want to import, but I get so many weird errors when I try to do it. I have pretty much added every folder and everything to my PYTHONPATH but nothing works.
Start contains main.py and main.kv where main.kv then points to the screenmanger in ``Prelogin. Prelogin contains some files that consist of labels and text about the app and then points to the screenmanger in FirebaseLoginScreen. The FirebaseLoginScreen contains a lot of files for the login system.
Yes, I'will give you and example with this folder structure
-Project\
--main.py
--main.kv
--folder1\
----window1.py
----window1.kv
--folder2\
----window2.py
----window3.py
--folder3\
----window4.py
----window4.kv
folder\window1.py will be like
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang.builder import Builder
from kivy.uix.widget import Widget
kivy.require("1.11.1")
# Builder is neccesary to work with multiple files
Builder.load_file("folder1/window1.kv")
class login(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
class login_window(App):
def build(self):
return login()
if __name__=="__main__":
aplicacion=login_window()
aplicacion.run()
folder1\window1.kv is not necessary to specify.
main.py will be like
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang.builder import Builder
from kivy.uix.widget import Widget
# Import files like
from folder1.window1 import login_window
from folder2.window2 import example2_window
from folder3.window3 import example3_window
class manager(BoxLayout):
# Add screens to main
login_widget=login_window()
example2_widget=example2_window()
example3_widget=example3_window()
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Check kv file to understand these lines
self.ids.screen_login.add_widget(self.login_widget)
self.ids.screen_example2.add_widget(self.example2_widget)
self.ids.screen_example3.add_widget(self.example3_widget)
class main(App):
def build(self):
return manager()
if __name__ == "__main__":
main().run()
main.kv will be like (Which has the ScreenManager!)
#:kivy 1.11.1
<manager>:
id: main_window
ScreenManager:
id: scrn_mngr_main
Screen:
id: screen_login
name:'screen_login'
Screen:
id: screen_example2
name:'screen_example2'
Screen:
id: screen_example3
name:'screen_example3'
Now, to control the program flow yo need to add these lines in window1.py
self.parent.current='screen_login'
self.parent.current='screen_example2'
self.parent.current='screen_example3'
Adapted from 3 videos of a tutorial on Youtube
The answer is simply "yes", there's nothing special about the import parsing in kv language, or in Python when using Kivy. If you're having issues, it's because what you're attempting is wrong or insufficient, which isn't possible to debug from the amount of information you've given.
If you'd like to follow it up, post another question with an example that you think should work given your PYTHONPATH manipulations.

Loading one line of text from file to Kivy label

I want to make a simple program that is just showing definitions that are stored in text file.One label and button to show next definition. I try to do it with documentation but i cannot find how to load text into label. Can someone show me to some good resources or code samples ?
My code for now (i want to build in on top of example from kivy website):
import kivy
kivy.require('1.9.0')
from kivy.app import App
from kivy.uix.label import Label
class MyApp(App):
def build(self):
return Label(text = 'Hello world')
if __name__ == '__main__':
MyApp().run()
The easiest way to update widgets in the UI are by binding to their properties. This can be done in code, but the real power of kivy in my opinion comes from using it's declarative UI language. Using kv, you get automatic binding.
Here is a quick example of what you might do:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import StringProperty
kv = '''
BoxLayout:
orientation: 'vertical'
Label:
text: app.text
Button:
text: 'click me'
on_press: app.clicked()
'''
class MyApp(App):
text = StringProperty("hello world")
def build(self):
return Builder.load_string(kv)
def clicked(self):
self.text = "clicked!"
if __name__ == '__main__':
MyApp().run()
In the kv description of the UI, you tell kivy that you want the text on the Label to be bound to a StringProperty on the app which you defined on the class. The auto-binding means that anytime you set a value to that property (like in the clicked function), the UI will update with the new value automatically.

Categories