kvlang : Builder.load_string issue - kivy

I was just trying to make a small hello world program as given below. Why nothing gets displayed.
# File: main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.base import runTouchApp
KV = '''
# File: clock.kv
<aClock#BoxLayout>:
orientation: 'vertical'
Label:
color: 1,0,1,1
text: '00:00:00'
<myClock> :
aClock:
'''
class myClock(App):
pass
if __name__ == '__main__':
Builder.load_string(KV)
myClock().run()

from kivy.app import App
from kivy.lang import Builder
KV = '''
<AClock#BoxLayout>:
orientation: 'vertical'
Label:
color: 1,0,1,1
text: '00:00:00'
AClock: # This is going to be the return value of Builder.load_string()
'''
class MyClock(App):
def build(self):
return Builder.load_string(KV)
if __name__ == '__main__':
MyClock().run()
App is not a widget, so you can't do this:
<myClock> :
aClock:
And in Kivy, PEP8 is constraint rather than style. All widgtes' class name must starts with upper case. So aClock must be AClock.
And you should implement build() and make it return a widget. (There is a case that build() doesn't have to return a widget, though.)
separating into two files
# myclock.kv
<AClock#BoxLayout>:
orientation: 'vertical'
Label:
color: 1,0,1,1
text: '00:00:00'
AClock:
# main.py
from kivy.app import App
class MyClock(App):
pass
if __name__ == '__main__':
MyClock().run()

Related

Using builder in Kivy

I want to create a simple app that has GUI with a button that allows me to vibrate an android phone. Im using a .kv file for the layout and the Builder in my .yp file
.py file:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.lang import Builder
from plyer import vibrator
class AndroidApp(GridLayout):
def vibrate(self):
vibrator.vibrate()
kv = Builder.load_file("android_app.kv")
class MainApp(App):
def build(self):
return kv
if __name__ == "__main__":
MainApp().run()
.kv file:
#:import utils kivy.utils
<AndroidApp>:
FloatLayout:
canvas.before:
Color:
rgb: utils.get_color_from_hex("#ffffff")
Rectangle:
size: self.size
pos: self.pos
GridLayout:
rows: 1
cols: 2
Label:
text:"Android Vibrate"
Button:
text:"Android Vibrate"
on_press:
root.vibrate()
When I try to run the app, I get the following error:
enter image description here
This should be straightforward app but somehow I find a way to make it crash. The android_app.py & .kv files are in the same folder. Any ideas why the window wont be created? I aprreciate any help.
Thanks,
Alex
You have to return the main class, not the result from building. Here I'm using Builder.load_string() for convenience, and not importing player, but otherwise the only substantive change is that I changed return kv to return AndroidApp()
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.lang import Builder
KV = '''
#:import utils kivy.utils
<AndroidApp>:
FloatLayout:
canvas.before:
Color:
rgb: utils.get_color_from_hex("#ffffff")
Rectangle:
size: self.size
pos: self.pos
GridLayout:
rows: 1
cols: 2
Label:
text:"Android Vibrate"
Button:
text:"Android Vibrate"
on_press:
root.vibrate()
'''
class AndroidApp(GridLayout):
def vibrate(self):
vibrator.vibrate()
Builder.load_string(KV)
class MainApp(App):
def build(self):
return AndroidApp()
if __name__ == "__main__":
MainApp().run()

kivy : kvlan register fails

I am trying to register a root (AClock) defined in kvlang with a python class 'AClock). Then I am running the app. But nothing shown. What is my mistake please ?
# File: main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.factory import Factory
from kivy.uix.boxlayout import BoxLayout
KV = '''
AClock:
orientation: 'vertical'
Label:
color: 1,0,1,1
text: '00:00:00'
'''
class AClock(BoxLayout):
pass
class myApp(App):
pass
Factory.register('Aclock',cls=AClock)
if __name__ == '__main__':
Builder.load_string(KV)
myApp().run()
The load_string() method returns a Widget (if the string defines a root Widget as your does), but you must return that Widget in the build() method of the App.
Here is a modified version of your code that dos that:
# File: main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
KV = '''
AClock:
orientation: 'vertical'
Label:
color: 1,0,1,1
text: '00:00:00'
'''
class AClock(BoxLayout):
pass
class myApp(App):
def build(self):
return Builder.load_string(KV)
if __name__ == '__main__':
myApp().run()

how to pass widget dimensions to __init__

Apologies if this question has an obvious answer but I have been unable to find a solution for some time now. A widget in my app has a 'graph' that is defined in terms of the widget's dimensions. I can dynamically update the 'graph' from kv because I have access to the widget's dimensions there. However I would like to define a default 'graph', also in terms of the widget's size, that appears at startup. I do not know how to pass the widget's dimensions to the __init__ function. Here is my boiled down example:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.properties import ListProperty
Builder.load_string('''
#:kivy 1.9.2
<MainWidget>:
BoxLayout:
size_hint_x: 20
orientation: 'vertical'
ToggleButton:
text: 'WF1'
state: 'down'
allow_no_selection: False
on_press:
root.line_points = [waveform.x, waveform.top, waveform.right, waveform.y]
root.event_handler()
group: 'lhs_buttons'
ToggleButton:
text: 'WF2'
allow_no_selection: False
on_press:
root.line_points = [waveform.x, waveform.y, waveform.right, waveform.top]
root.event_handler()
group: 'lhs_buttons'
BoxLayout:
size_hint_x: 80
Button:
id: waveform
canvas:
Line:
points: root.line_points
''')
class MainWidget(BoxLayout):
line_points = ListProperty()
def __init__(self, **kwargs):
super(MainWidget, self).__init__(**kwargs)
#self.line_points = [waveform.x, waveform.top, waveform.right, waveform.y]
def event_handler(self):
print "event"
class MyApp(App):
def build(self):
return MainWidget()
if __name__ == '__main__':
MyApp().run()
I suppose a partial solution would be to trigger the on_press event in __init__ for one of the buttons , but I have been unable to figure out how to do that. I am new to Python and to Kivy.
One approach will be to bind line_points like this:
<MainWidget>:
line_points: self.calc_line_points(waveform.x, waveform.y, waveform.right, waveform.top)
...
And calc_line_points will be defined such as :
def calc_line_points(self, x, y, right, top):
return [ x, top, right, y] #put more logic here ...

Kivy markup messing up opening square bracket

Suppose a Label widget has a text 'abcd[', it prints out the correct thing on the output screen as expected. But when I set the markup for that Label widget to True, it prints out 'abcd[[/color]'. How do I overcome this? I found out one possible fix by adding '\n' after the opening bracket in the text. But since I have many widgets in a row close to each other, the newline is pretty visible and it looks kinda ugly.
For this example, I'm using Button instead of Label.
This is the output for
Button:
markup: True
text: 'abcd\n['
And this is the output for
Button:
markup: True
text: 'abcd\n[\n'
As I said, adding a newline makes it look ugly and the difference in text level between the nearby widgets look very visible.
This can be solved by using escape_markup or replacing '[' with '&bl;'.
Method 1: Using the escape_markup.
from kivy.app import App
from kivy.lang import Builder
kv = ('''
#:import escape kivy.utils.escape_markup
Label:
markup: True
text: 'abcd{}'.format(escape('['))
''')
class mainApp(App):
def build(self):
return Builder.load_string(kv)
if __name__ == '__main__':
mainApp().run()
Method 2: Character replacement.
from kivy.app import App
from kivy.lang import Builder
kv = ('''
#:import escape kivy.utils.escape_markup
Label:
markup: True
text: 'abcd&bl;'
''')
class mainApp(App):
def build(self):
return Builder.load_string(kv)
if __name__ == '__main__':
mainApp().run()
Now, if you want to change color of '[' you have to do it like this:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.properties import StringProperty
kv = (
'''
#:import escape kivy.utils.escape_markup
<L>:
markup: True
text: self.hidden_text
<B>:
Button:
text: 'press'
on_press: root.lel()
L:
id: lol
hidden_text: 'abcd{}'.format(escape('['))
markup: True
B
'''
)
class L(Label):
hidden_text = StringProperty('')
class B(BoxLayout):
def lel(self):
self.ids.lol.text = '{}[color=#E5D209]{}[/color]'.format(self.ids.lol.hidden_text[:4], self.ids.lol.hidden_text[4:])
class color(App):
def build(self):
return Builder.load_string(kv)
if __name__ == "__main__":
color().run()
Notice what I did in lel() in B class. To change to color of '[', I typed hidden_text[4:] instead of hidden_text[4]. This is because when you do escape('['), all it does is it replaces '[' by '&bl;'. So, when you use hidden_text[4], you'll get this output:
But if you use hidden_text[4:], it covers the characters after & until it reaches the semi-colon.
To know why I used StringProperty on the Label's text, read here.

How to create simple Kivy app (form with field name)

How to create simple Kivy app?
If user type text into field "Name" using keyboard like on android
phone, this name is display
I need this to learn
Here's as simple an example as i can figure out.
Here's the KVLANG code.
<LblTxt#BoxLayout>:
orientation: 'horizontal'
lblTxtIn: 'default'
theTxt: iAmTxt
Label:
text: root.lblTxtIn
TextInput:
id: iAmTxt
text: 'txt'
<MyLayout#BoxLayout>:
orientation: 'vertical'
LblTxt:
id: lt0
lblTxtIn: 'LblTxtInput0'
LblTxt:
id: lt1
lblTxtIn: 'LblTxtInput1'
LblTxt:
id: lt2
lblTxtIn: 'LblTxtInput2'
Button:
text: 'print LblTxtInput [0, 1, 2]'
on_release: print lt0.theTxt.text, lt1.theTxt.text, lt2.theTxt.text
MyLayout
Here's the Python code.
import kivy
kivy.require('1.8.0') # replace with your current kivy version !
from kivy.app import App
from kivy.lang import Builder
from kivy.config import Config
from kivy.core.window import Window
Window.size = (400,130)
from kivy.uix.boxlayout import BoxLayout
class LblTxt(BoxLayout):
from kivy.properties import ObjectProperty
theTxt = ObjectProperty(None)
class MyApp(App):
def build(self):
self.root = Builder.load_file('simpleForm.kv')
return self.root
if __name__ == '__main__':
MyApp().run()
Here's a run screenshot. It will print a b c to the command line when the 'print LblTxtInput [0, 1, 2]' button is released.
I hope this helps you out.

Resources