I have noticed that most examples I foind online don't have an external .kv file. They define all the instances internally. However they also say that having an external .kv file is a good practice. Which is better to do? If having external .kvfiles are better, then how am I supposed to use the code which uses internal code and turn it into external .kv files?
For example, doing this ->
from kivy.app import App
from kivy.uix.scatter import Scatter
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
class TutorialApp(App):
def build(self):
b = BoxLayout(orientation='vertical')
t = TextInput(font_size=150,
size_hint_y=None,
height=200,
text='default')
f = FloatLayout()
s = Scatter()
l = Label(text='default',
font_size=150)
t.bind(text=l.setter('text'))
f.add_widget(s)
s.add_widget(l)
b.add_widget(t)
b.add_widget(f)
return b
if __name__ == "__main__":
TutorialApp().run()
Instead of-
<ScatterTextWidget>:
orientation: 'vertical'
TextInput:
id: my_textinput
font_size: 150
size_hint_y: None
height: 200
text: 'default'
FloatLayout:
Scatter:
Label:
text: my_textinput.text
font_size: 150
"Internal" usage of kv is through Builder class, which allows you even to load external file. Those examples are worded in a Builder.load_string(...) way because it's way simpler to have a small example in one place in one file.
How to convert it to the external one? Simple, copy&paste the string from Builder.load_string() into a separate .kv file with a name of your class that inherits from App(your main class with build()) and that's it. It'll load the same thing from the external file.
Why it's better or worse? Isn't any of that actually. It's like comparing "java" and python style i.e. either putting everything out of your file and having basically this construction a'la java, where the main file contains this:
class This(something):
SpecialClass.doThis()
AnotherClass.doThat()
and other classes(or different things) are in separate files. Or this construction:
class Special(...):
...
class Another(...):
...
class This(something):
Special.do_this()
Another.do_that()
Both of them are useful and you'll find yourself working with a mix between them. It's about transparency and clearness of your code, but maybe you don't want to have a hundred of files... or a 2MB main.py, pretty much compromise of how do you decide to code.
Edit:
The python vs kv is a funny "fight", but except a for(and while?) loop you can pretty much do everything necessary inside kv in such an easy way! Kv is here to make writing easier e.g. remove too much stuff like add_widget() or basically making an empty class just to rename a widget or to change its size for using it in one place.
With python in a 500line file without kv you won't do that much as with 100 extra lines in kv. The documentation has important parts in python and maybe it's even targeted for users who can't/don't want to use kv. Also this and all examples highly depend on the author of an example and that particular part of the docs.
Which returns me back to the java vs python style of coding I mentioned before. It's pointless to do complicated stuff just because you think it'll feel/look better if you can do it cleaner and more readable i.e. don't just go one way if you have a tool that increase your speed of coding exponentially. Find the middle way.
Related
I cant seem to find any examples of interfacing kivy mapview with python for things like moving the map or adding markers from the python side of things.
What I mean is I would like to eventually pull data from a CSV file and have it control the information on screen. This is straightforward with labels and buttons. But I'm not able to make the same methides work with mapview.
All references I find on line controlled all logic inside the .KV files. None of the examples I find use python to reach in and change things inside the .KV file.
This is the best example I have tested so far. but as you can see all logic is inside the .KV file. https://github.com/kivy-garden/mapview/blob/develop/examples/map_browser.py
I want to be able to send data from the python side to the .kv side and have it update the screen as its being sent. Like Lat and lon positions, markers and so on.
What I am use to doing something like this.
Python side
from kivy_garden.mapview import MapView
from kivy.lang import Builder
from kivymd.app import MDApp
class MainApp(MDApp):
def build(self):
return Builder.load_file('MapViewApp.kv')
MainApp().run()
MapViewApp.kv
MapView:
id: map
lat: 36
lon: -115
zoom: 10
Was wondering why the Kivy code kept on showing me the same black window despite doing some updates on the kv file. Then noticed I had a typo on the buidl() method.
From the docs "...implementing its build() method so it returns a Widget instance (the root of your widget tree)
...", you have to implement the method.
Why does this code run and give the default black window?
# game.py
from kivy.app import App
from kivy.uix.widget import Widget
class Game(Widget):
pass
class GameApp(App):
def buidl(self):
return Game()
GameApp().run()
The kv file
#game.kv
<Game>:
canvas:
Color:
rgb: .5,.5, 1.0
Rectangle:
pos: 0,0
size: self.size
Running kivy 1.11.1 python 3.7
Kivy apps have a default build() method, which you can see here; it just returns an empty widget. Generally kivy has two methods to create the root widget tree, either through overriding build() or by defining a root widget in a kv file. For more information see the documentation on creating an application.
Your quote can be found in kivy basics, before your quoted sentence:
Creating a kivy application is as simple as:
I guess the authors decided to keep the basic tutorial easy and did not mention the default implementation of build, as it doesn't really do anything useful. They also omitted the kv way of defining the root widget; again I would guess to not overwhelm the reader in this first introduction.
I'm using gestures in all of my screens, and I cannot use a screen manager class to manage my screens, or so I believe. I can navigate the .kv file by using manger.current = 'some_screeen' but cannot in the .py file.
I've been trying Runner().ids.manager.current = 'some_screen' in the .py file but it doesn't work. There isn't even an error thrown. The screen doesn't change at all.
Essential Code (for the sake of brevity):
class Runner(gesture.GestureBox):
pass
MyApp(App):
def build(self):
return Runner()
Then in the KV file, I'm creating the screen manager.
<Runner>:
ScreenManager:
id: manager
Screen:
name: 'main_screen'
Button:
on_press:
manager.current = 'screen1'
Screen:
name: 'screen1'
Button:
on_press:
manager.current = 'home_screen'
I've been trying Runner().ids.manager.current = 'some_screen' in the .py file but it doesn't work. There isn't even an error thrown. The screen doesn't change at all.
It works fine, it just doesn't do what you believe. When you write Runner() you get a new instance of the Runner class, with its own children including its own ScreenManager. This one has nothing to do with the one you're displaying in your gui. When you set its current property the ScreenManager will dutifully change the screen, it's just you have no way to see that.
What you actually want is to change the current property of the widget that you are displaying in your gui. The best way to do this depends on the context, which you have omitted (always try to provide a full runnable example, it isn't clear what your failing code looked like). However, in this case the Runner instance is your root widget which is accessible with App.get_running_app().root, so you can write App.get_running_app().root.ids.manager.current = 'some_screen'. Again, there might be neater ways to do it depending on how you structure your code, but this is always an option.
How do I use the kivy module garden.graph inside the kv file? I only found documentation that explained how to use it in the main python script.
I imported the kivy.garden.graph in the python file, and I can add the Graph inside the kv file, but I didn't find any documentation how to set the size, plots etc.
Graph:
id: graph_test
plot: MeshLinePlot
this gives an error since MeshLinePlot is not defined, though I imported it on the python side.
any help would be highly appreciated, maybe we could then add this info to the graph's github readme as well.
Building on the answer from piwnk:
I added this to the .kv file:
#:import MeshLinePlot kivy.garden.graph.MeshLinePlot
<SetGraph>:
graph_test : graph_test
Graph:
id: graph_test
plot: MeshLinePlot
xlabel:'X'
ylabel:'Y'
x_ticks_minor:5
x_tics_major:25
y_ticks_major:1
y_grid_label:True
x_grid_label:True
padding:5
x_grid:True
y_grid:True
xmin:-0
xmax:100
ymin:-1
ymax:1
pos: 0, root.height / 6
size: root.width * 2 / 3 , root.height * 18 / 24
In main.py, I added:
from math import sin
from kivy.garden.graph import Graph, MeshLinePlot
class SetGraph(Widget):
graph_test = ObjectProperty(None)
update_graph(self):
plot = MeshLinePlot(color=[1, 0, 0, 1])
plot.points = [(x, sin(x / 10.)) for x in range(0, 101)]
self.graph_test.add_plot(plot)
class graphLayoutApp(App):
def build(self):
disp = SetGraph()
disp.update_graph()
return disp
if __name__ == '__main__':
graphLayoutApp().run()
I have changed my original tested solution to more descriptive names. Hopefully, I have not made any mistakes. Let me know if the solution is not complete.
The answer from Mattis Asp was very helpful but didn't quite work for me. I am new to this, so maybe these things are too obvious to need stating. But in case it helps someone else at my level, I had to:
Indent the properties under the Graph: declaration in the kv file (to get around an "invalid data after declaration" exception from the kv parser.
Add these includes:
language: lang-py
from kivy.properties import ObjectProperty
from kivy.app import App
from kivy.uix.widget import Widget
to the top of the python file.
Name the kv file to match the app class definition, so: graphLayout.kv (I had called it graph.kv so it was just ignored -- newbie mistake!)
I was getting "invalid property name" for graph_test : graph_test. So I commented that out and used the id instead, changing the line
self.graph_test.add_plot(plot)
to:
self.ids["graph_test"].add_plot(plot)
I bet at least some of these changes have to do with version differences in kivy so, for clarity, I am using kivy 1.9.1 and python 2.7.13.
Had the same problem. Here's the solution:
Generally, according to kivy documentation, in kv file:
#:import name x.y.z
is equivalent to:
from x.y import z as name
So you should use the following:
#:import MeshLinePlot kivy.garden.graph.MeshLinePlot
Worked in my case with Graph class but, to be honest, I didn't managed to add this plot to the graph yet.
I think inclement was on the right track. Using #:import should be able to import the file.
write this in the kv file:
#:import MeshLinePlot
it should be able to import the module, as the kv documentation shows, also
I've created a ScreenManager, and created several Screen instances for that ScreenManager.
I'd like each Screen to display a GridLayout class. For example, let's say you have:
class MainScreen(Screen):
...
class MainLayout(GridLayout):
...
When MainScreen is the active screen, I'd like MainLayout to be shown.
Is there a way to do this purely in python (i.e. without markup)? Thank you.
Is there a way to do this purely in python (i.e. without markup)? Thank you.
You never need to use kivy language (I assume that's what you mean by markup), though it's highly recommended where possible because it makes lots of stuff easier.
Nonetheless, to actually answer your question, all you have to do is add your gridlayout widget to your screen widget, something like
mainscreen = MainScreen()
mainlayout = MainLayout()
mainscreen.add_widget(mainlayout)
Then when you set the current screen in your screenmanager to be mainscreen, you should see the GridLayout.
Edit: In case it's unclear, this is in general the way you add widgets to other widgets. When you see an example in kv language like
<MyScreen>:
GridLayout:
...
...ultimately that gets translated to something much like the above example code - an instance of MyScreen is created and a GridLayout is added to it with add_widget.