Get children from kv file before it is applied - python

I have a kv file or string for example
KV = '''
<BoxLayout>
Label
Label
Button
MyButton
'''
and I would like to get children list like
['Label', 'Label', 'Button', 'MyButton']
before this kv string is applied (before Label, Button and MyButton instances created).
How to do this?
Can I use kivy.lang.Parser for that?
I suppose kivy Parser does something like that, but I didn’t figure out how to use it - the documentation is scarce on this topic, and the source code looks confusing.
I'm planning to create a widget event, named something like on_kv_pre.

You can use 'Builder.match_rule()` for your situation:
from kivy.lang import Builder
Builder.load_string('''
<BoxLayout>
Label
Label
Button
MyButton
''')
ret = Builder.match_rule_name('BoxLayout')
for child in ret[0].children:
print(child.name)
May be a bit more complicated for real rules, but maybe this can get you started.

Related

Modify variables within Python through a button in Kivy

I have an application that at a certain point needs to check if a variable within its Python code has one value or another, and also requires a button capable of changing that variable from one value to another to also save that information in a text file and that the variable loads it the next time the application is started.
Here I prepared a small program to exemplify what I am looking for:
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang.builder import Builder
from kivy.core.window import Window
Window.size=(200,200)
data=open('data.txt','r') #Let's say there is a text file called "data" that contains the number 0
Builder.load_string("""
<Screen>
BoxLayout:
orientation:'vertical'
size:root.width,root.height
Label:
text:'Switch On' if app.var==1 else 'Switch Off' #Initially then the label should say "Switch Off"
Button:
text:'Switcher'
on_release: #Change app.var from 0 to 1 or from 1 to 0
""")
class Screen(Widget):
pass
class App(App):
def build(self):
self.var=data.read()
#Save the changes made by the Button
return Screen()
if __name__=='__main__':
App().run()
How can I do this or something similar? A better and simpler way?
You should define a function and bind it to a button. The documentation can really help you here! you can bind your function using my_button.bind(on_press=on_button_click) where:
def on_button_click():
this.my_var += 1
Let me know if you think the documentation is unclear!

Create Kivy widgets without using kv language?

How to create Kivy widgets without using kv language and .kv files? I am new to kivy. I usually use Tkinter, but i found out that Kivy is good for creating Android apps, so I am learning how to use it.
I know that Kivy widgets are made using .kv files, but i would like to create them like widgets in Tkinter.
# Creating widgets in tkinter:
# We'll assume tkinter is imported as tk
label_1 = tk.Label(text='Hello World', bg='white')
label_1.pack()
# Creating widgets using Kivy in .py file
label_1 = Label(text='Hello World') # eg. Not able to set color!
add_widget(label_1)
# Creating widgets using kv language
Label:
text: "Hello World"
color: 1,0,0,1
So, is there any way to make Kivy widgets completely in python? How could i set Kivy label color directly in python?
Thanks.
Here's an example of Kivy application without using kv lang:
from kivy.app import App
from kivy.uix.label import Label
class TestApp(App):
def build(self):
return Label(
text='Hello, world',
color=(1, 0, 0, 1)
)
TestApp().run()
Basically build method of kivy.app.App instance has return a main widget object, in this case a kivy.uix.Label instance. To have more complex widget you should create a subclass of some layout class and then add widgets using add_widget method.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
class TestWidget(BoxLayout):
def __init__(self, **args):
super(TestWidget, self).__init__(**args)
label = Label(
text='Hello, world',
color=(1, 0, 0, 1))
self.add_widget(label)
class TestApp(App):
def build(self):
return TestWidget()
TestApp().run()
I'd like to encourage you to use kv lang anyway, since it makes the code simplier.
It is completely possible to use python exclusively to write apps with kivy. I began using kivy about 6 month ago and quickly abandoned the kv files. I do not like "magical" code in the background making bindings. I know this is a preference thing and now that I know how the widgets and layouts work, it may be actually quicker to use the kv language. Things you need to do manually are keep handles between widgets throughout the widget tree and setup bindings using the kivy properties. There is a lot to learn but once you start getting it, the possibilities are endless. Most examples you find are very simple and I feel the documentation is very limited. I feel that if you are comfortable with python, you will actually learn more about how kivy works if you stay in python.
I'm marking Nykakin's answer as correct, but I just thought I'd share some details.

Using external .kv file vs doing things internally?

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.

Events passing in python with kivy

I am new to python and kivy, I am trying to clear all widgets in a host when any button is pressed, my code is as follows:
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.label import Label
spots={}
class spot(Button):
'''
classdocs
'''
def __init__(self, **kwargs):
'''
Constructor
'''
super(spot,self).__init__(**kwargs)
self.ismine=False
if 'text' in kwargs:
self.text=kwargs['text']
else:
self.text="X"
def on_press(self, *args, **kwargs):
#return Button.on_press(self, *args, **kwargs)
game.spottouched(self, self.text)
class game(BoxLayout):
def __init__(self,**kwargs):
super(game,self).__init__(**kwargs)
self.m=minesholder(rows=25, cols=25)
self.add_widget(self.m)
self.attachtogrid()
def attachtogrid(self):
self.m.clear_widgets()
spots.clear()
for r in range(0,25):
for c in range(0,25):
idd=str(r)+","+str(c)
spots[idd]=idd
self.m.add_widget(spot(text=idd))
def spottouched(self,spotid):
#popup=Popup(title=spotid)
#popup.open()
self.clear_widgets()
The last line is for clearing the widgets, and spot class has the on_press event. I am trying to pass the event of on_press even of button on app to a holding boxlayout (game class), could you please tell/point me in right direction to lean on how pass the events?
Thank you.
I think this is what you want, based on your comment on #inclement's answer: Get rid of the on_press method on your button. You want to bind to the event instead - this will work more like WPF.
for r in range(0,25):
for c in range(0,25):
idd=str(r)+","+str(c)
spots[idd]=idd
s = spot(text=idd)
self.m.add_widget(s)
s.bind(on_press=self.spottouched)
So self.spottouched is like an event handler for the spots on_press event.
s.bind(on_press=self.spottouched)
is kind of like this:
AddHandler s.Click, AddressOf spottouched
Note that adding the handler this way, the handler will receive a single argument which is the spot instance. You can get spot.text from that instance.
game.spottouched(self, self.text)
You've misunderstood how python classes work. You want to clear the widgets from the instance of your game that you're actually using - something you created by writing game(). With a reference to that, you need to do instance.spottouched(spotid) to call the method of that instance.
What you're doing instead is calling the method via the class definition, which really makes no sense here and you don't want to do it. Actually, you basically never want to do this.
Anyway, the way to fix this depends on how you actually constructed your app, but it boils down to you need to keep a reference to the game instance somewhere that the spot instance can see it. The best way depends on the rest of your code, but you can always do it via the app class, e.g.
App.get_running_app().game = game()
and later
App.get_running_app().game.spottouched(self.text)
This is just an example, it might be a bad way to do things sometimes.
Also, begin your widget names with capital letters! kv language uses this to identify them, you will hit problems using it if you don't name them this way. It's also a strong python convention worth following.

Associating Screens with GridLayout classes in kivy

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.

Categories