I was under the impression that svg worked like a canvas instruction.
However, I am unable to get the svg to change color. Part of the code below is using the code provided in the sample at Kivy's github.
with self.canvas:
Color(0.3833, 1.0, 0.0)
Entire Code
import sys
from glob import glob
from kivy.uix.scatter import Scatter
from kivy.app import App
from kivy.graphics.svg import Svg
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from random import randint
from kivy.graphics import *
Builder.load_string("""
<SvgWidget>:
do_rotation: False
<FloatLayout>:
id: svg_holder
canvas.before:
Color:
rgb: (1, 1, 1)
Rectangle:
pos: self.pos
size: self.size
""")
class SvgWidget(Scatter):
def __init__(self, filename, **kwargs):
super(SvgWidget, self).__init__(**kwargs)
with self.canvas:
Color(0.3833, 1.0, 0.0)
svg = Svg(filename)
self.size = svg.width, svg.height
def remove(self):
print('remove', self)
class SvgApp(App):
def build(self):
self.root = FloatLayout()
for i in range(6):
svg = SvgWidget('star.svg', size_hint=(None, None))
self.root.add_widget(svg)
svg.scale = randint(1,4)
svg.center = randint(0,Window.width), randint(0,Window.height)
if __name__ == '__main__':
SvgApp().run()
I don't think what you asking for is possible since the Svg contains Color instructions itself, so it overrides yours... Maybe your code can use an overlay with opacity to make the desired effect
Related
I have just started learning kivy. Please bear with me in case of my absurdity and stupidity.
Here just copy pasted the code from official site.
import kivy
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()
And the output is in the link.
What I essentially wanted to know that how I can change the background (currently black) to some different color.
I have read some parts of documents ,Found documentation for changing color of widget but screen (probably not the accurate word).
I would really appreciate the advices and suggestions.
Thanks in advance.
You can use the canvas of the Label to paint a background color like this:
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
class MyLabel(Label):
pass
Builder.load_string('''
<MyLabel>:
canvas.before:
Color:
rgba: 1,0,0,1
Rectangle:
pos: self.pos
size: self.size
''')
class MyApp(App):
def build(self):
return MyLabel(text='Hello world',)
if __name__ == '__main__':
MyApp().run()
To do this without using the kv language is more complicated because you must set up the binding to pos and size that the kv language does for you automatically. Here is the equivalent without using kv:
from kivy.app import App
from kivy.graphics import Color, Rectangle
from kivy.uix.label import Label
class MyLabel(Label):
def __init__(self, **kwargs):
super(MyLabel, self).__init__(**kwargs)
with self.canvas.before:
Color(1, 0, 0, 1)
self.rect = Rectangle(pos=self.pos, size=self.size)
def on_pos(self, *args):
self.rect.pos = self.pos
def on_size(self, *args):
self.rect.size = self.size
class MyApp(App):
def build(self):
return MyLabel(text='Hello world',)
if __name__ == '__main__':
MyApp().run()
For more information about the canvas, see the documentation
As it can be understood from the code, I want to assign a new position to the ball on the screen when a certain pixel range is clicked, that is, I want to change its current position. I tried this with self.ids.widget.pos and things like that but didn't get any results.
There is also one more thing I want to ask regardless of the subject. While I define this ball on the screen, the ball does not appear in the center of the screen, I adjusted it myself. Normally I use a 300 width and 500 height screen for the application, but I was able to achieve this by giving 175 width and 275 height values to get this ball right in the middle. How can I better adjust this?
I have removed the parts I don't need to shorten the code, but I can add them later if you want.
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.screenmanager import Screen
from kivymd.app import MDApp
from time import sleep, time
from random import randint
from kivy.properties import NumericProperty
from kivy.uix.widget import Widget
from kivy.clock import Clock
from kivy.graphics import Color
Window.size = (300, 500)
helper = """
ScreenManager:
MenuScreen:
ReflexScreen:
MainScreen:
AimScreen:
<Ball>:
canvas:
Ellipse:
pos: self.pos
size: 50,50
<AimWidget>
ball: pong_ball
Ball:
id: pong_ball
center_x: 175
center_y: 275
<AimScreen>
id: aim
name: 'aim'
AimWidget:
id: widget
"""
class Ball(Widget):
pos_x = NumericProperty()
pos_y = NumericProperty()
class AimWidget(Widget):
def update(self, dt):
pass
class AimScreen(Screen):
aim = AimWidget()
Clock.schedule_interval(aim.update, 1.0/60.0)
def on_pre_enter(self, *args):
pass
def on_touch_down(self, touch):
if (touch.x >= 125 and touch.x <= 175) and (touch.y >= 225 and touch.y <= 275):
pass
Here is a modified version of your code:
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.screenmanager import Screen
from kivy.uix.widget import Widget
from kivy.clock import Clock
Window.size = (300, 500)
helper = """
ScreenManager:
# MenuScreen:
# ReflexScreen:
# MainScreen:
AimScreen:
<Ball>:
size_hint: None, None
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
<AimWidget>
ball: pong_ball
Ball:
id: pong_ball
center: root.center
<AimScreen>
id: aim
name: 'aim'
AimWidget:
id: widget
"""
class Ball(Widget):
pass
class AimWidget(Widget):
def update(self, dt):
pass
class AimScreen(Screen):
aim = AimWidget()
Clock.schedule_interval(aim.update, 1.0 / 60.0)
def on_pre_enter(self, *args):
pass
def on_touch_down(self, touch):
if (touch.x >= 125 and touch.x <= 175) and (touch.y >= 225 and touch.y <= 275):
ball = self.ids.widget.ball
ball.center = touch.pos
The Ball widget size was the default (100, 100), so the canvas instructions were drawing the circle in the lower left corner of the Ball widget. Added size to make it (50,50), the desired size of the circle.
The canvas instructions for the Ball, now just reference the size and pos of the Ball widget.
The Ball in the AimWidget is initially positioned using center. This works because the Ball is now the same size as the Ellipse.
The on_touch_down() method can now just set the center of the Ball to the touch.pos.
Im am trying to display my arduino data into my kivy window under label but it says an error " ValueError: Label.text accept only str". Anyone able to help?
Below is my python kivy file
test.py file
import serial
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.button import Button
class MainWindow(Screen):
def tm(self):
while (1):
with serial.Serial('COM4', 9600) as ser:
x = ser.readline()
return str(x)
ser.close()
class WindowManager(ScreenManager):
pass
hi = Builder.load_file("test.kv")
class MyMainApp(App):
def build(self):
return hi
if __name__ == "__main__":
MyMainApp().run()
test.kv file
WindowManager:
MainWindow:
<MainWindow>:
Label:
size: 75, 50
size_hint: None, None
pos_hint: {'right': 1, 'bottom': 1}
background_color: (1.0, 0.0, 0.0, 1.0)
text: root.x
I see a few problems in your example.
Be careful naming your variables. x for example, is almost always a NumericProperty of the widget already.
When you reference an attribute in python, use self, or the widgets x in your case wil not change.
When you have a loop in kivy, you will block the application. So you either use Clock or threading. I will suggest threading instead of Clock when using serial, because readline() will also block the app if waiting for an input.
You never actually call tm()
So, heres what I would change in MainWindow class, and a few more imports.
import serial
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.button import Button
from kivy.properties import StringProperty
from kivy.clock import mainthread
import threading
KV = """
WindowManager:
MainWindow:
<MainWindow>:
Label:
size: 75, 50
size_hint: None, None
pos_hint: {'right': 1, 'bottom': 1}
background_color: (1.0, 0.0, 0.0, 1.0)
text: root.label_text
"""
class MainWindow(Screen):
label_text = StringProperty("")
def __init__(self, **kwargs):
super(MainWindow, self).__init__(**kwargs)
threading.Thread(target=self.tm).start()
def tm(self):
while (1):
with serial.Serial('COM4', 9600) as ser:
value = ser.readline()
ser.close()
self.update_label(value.decode("ascii").strip())
#mainthread
def update_label(self, value):
self.label_text = value
class WindowManager(ScreenManager):
pass
class MyMainApp(App):
def build(self):
return Builder.load_string(KV)
if __name__ == "__main__":
MyMainApp().run()
Be sure to use mainthread when changing any ui element in another thread. In this case when you change a stringproperty that will change the display.
What I try is part of a larger App but I posted here the minimum to understand my problem:
I want to change the size of a rectangle, and that this size depends on the root.height .
3 cases:
1) When I create a rectangle with "root.height/4" as a height, the size varies as I resize the root window. So far no problem
2) When I tried to assign "root.height/4" to an attribut and call this attribut when setting the size of my rectangle I get the following error:
"TypeError: a float is required".
Python file:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color
class BriqueApp(App):
def build(self):
return BriqueGUI()
class BriqueGUI(Widget):
pass
BriqueApp().run()
Kv file:
#:kivy 1.9.1
<BriqueGUI>:
h: root.height/4
color: (1,0,0,1)
canvas:
Color:
rgba: root.color
Rectangle:
size: (200, self.h)
pos: (0,0)
3) When I tried to reassign a value depending on, for example "root.height/2", when an event is call (here on_touch_down) : The size of my Rectangle changes but when I resize my windows my rectangle doesn't resize with it.
You can see the problem here loading the file, clicking in the main window, and changing its size.
Python file:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color
class BriqueApp(App):
def build(self):
return BriqueGUI()
class BriqueGUI(Widget):
def on_touch_down(self, touch):
self.h = self.height/2
BriqueApp().run()
Kv file:
#:kivy 1.9.1
<BriqueGUI>:
h: 20
color: (1,0,0,1)
canvas:
Color:
rgba: root.color
Rectangle:
size: (200, self.h)
pos: (0,0)
2) You have to create property in your Python code as well:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color
from kivy.properties import NumericProperty
from kivy.lang import Builder
Builder.load_string("""
<BriqueGUI>:
h: root.height/4
color: (1,0,0,1)
canvas:
Color:
rgba: root.color
Rectangle:
size: (200, self.h)
pos: (0,0)
""")
class BriqueApp(App):
def build(self):
return BriqueGUI()
class BriqueGUI(Widget):
h = NumericProperty(0.0)
BriqueApp().run()
3) You're only assigning once a value to your h property. What you need is bind method that allows you to associate a callback that will be called any time the property changes:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color
from kivy.properties import NumericProperty
from kivy.lang import Builder
Builder.load_string("""
<BriqueGUI>:
h: 20
color: (1,0,0,1)
canvas:
Color:
rgba: root.color
Rectangle:
size: (200, self.h)
pos: (0,0)
""")
class BriqueApp(App):
def build(self):
return BriqueGUI()
class BriqueGUI(Widget):
def on_touch_down(self, touch):
self.h = self.height/2
self.bind(height=self.set_h)
def set_h(self, instance, value):
self.h = value/2
BriqueApp().run()
I have a ver long text in kivy. I want adjustment dynamic height depend of qty of text.
My code is this.
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
class DynamicHeight(App):y
def build(self):
grid = gl = GridLayout(cols=1)
for i in range(3):
l = Label(text='Text a longer line line line line line line line line', halign='left',text_size=(300, None))
grid.add_widget(l)
return grid
DynamicHeight().run()
I want that height of label or height row of gridlayout adjustment according to the amount of text.
Although there are solutions proposed already, i feel they don't leverage the kivy way of doing things, and that this way is cleaner. What you need is to bind the text_size to the available width, and bind the height of the widget to the rendered texture size.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
class MyApp(App):
def build(self):
root = FloatLayout()
b = GridLayout(
cols=1,
pos_hint={
'center_x': .5,
'center_y': .5},
size_hint=(None, None),
spacing=20,
width=200)
b.bind(minimum_height=b.setter('height'))
root.add_widget(b)
for text_length in range(0, 80, 20):
l = Label(
text='word ' * text_length,
size_hint_y=None)
l.bind(width=lambda s, w:
s.setter('text_size')(s, (w, None)))
l.bind(texture_size=l.setter('size'))
b.add_widget(l)
return root
if __name__ == '__main__':
MyApp().run()
I found the solution, with help of thopiekar.
For those needing this. So far I have not found kivy do without this method
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class MultiLineLabel(Button):
def __init__(self, **kwargs):
super(MultiLineLabel, self).__init__( **kwargs)
self.text_size = self.size
self.bind(size= self.on_size)
self.bind(text= self.on_text_changed)
self.size_hint_y = None # Not needed here
def on_size(self, widget, size):
self.text_size = size[0], None
self.texture_update()
if self.size_hint_y == None and self.size_hint_x != None:
self.height = max(self.texture_size[1], self.line_height)
elif self.size_hint_x == None and self.size_hint_y != None:
self.width = self.texture_size[0]
def on_text_changed(self, widget, text):
self.on_size(self, self.size)
class DynamicHeight(App):
def build(self):
grid = GridLayout(cols=1,size_hint_x=None, width="300dp")
l=['This Text very long, should add multiple lines, automatically. This Text very long, should add multiple lines, automatically', 'One line']
for i in l:
l = MultiLineLabel(text=i)
grid.add_widget(l)
return grid
DynamicHeight().run()
And works perfectly!!!!!
text.size() method isn't changing height atribute of Label. If text is too long, it will overlap with content below:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
class DynamicHeight(App):
def build(self):
layout = GridLayout(cols=1, spacing=20)
l = Label(text='! '*100, text_size=(10, None), size_hint_y=None, height=10)
b = Button(text='...', size_hint_y=None)
layout.add_widget(l)
layout.add_widget(b)
return layout
DynamicHeight().run()
You need to calculate the heigh of the text and set it with height attribute manually. I don't know any nice and clean way to do this. Here is a dirty way:
before = label._label.render()
label.text_size=(300, None)
after = label._label.render()
label.height = (after[1]/before[1])*before[1] # ammount of rows * single row height
Example:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.scrollview import ScrollView
class DynamicHeight(App):
def build(self):
layout = GridLayout(cols=1, size_hint_y=None, spacing=20)
layout.bind(minimum_height=layout.setter('height'))
for i in xrange(1, 20):
l = Label(text='Text ' * (i*10), text_size=(300, None), size_hint_y=None)
# calculating height here
before = l._label.render()
l.text_size=(300, None)
after = l._label.render()
l.height = (after[1]/before[1])*before[1] # ammount of rows * single row height
# end
layout.add_widget(l)
root = ScrollView()
root.add_widget(layout)
return root
DynamicHeight().run()
Following up on tshirtman's answer, I created code for doing the same thing in kv-lang. It might be a bit clearer what's going on, since you don't need to analyse the callback functions.
What's happening is that the label's width gets set in accordance with the layout, while the height gets set to the texture size of the text. So as the text string gets longer, the texture can only grow in height, which we can apply above, by setting the height of the boxlayout to my_label.height.
# -*- coding:utf8 -*-
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.core.window import Window
Window.size = (400, 700)
PLACEHOLDER_TEXT = u'''The bindings illustrated in tshirtman's example are created automatically when using kv-lang.
Notice how the "id: my_label" allows us to access the Label's attributes in the BoxLayout's height declaration.'''
kv_string = """
<Example>:
orientation: 'vertical'
BoxLayout:
# colored background for affected area:
canvas.before:
Color:
rgba: 0.3, .4, .4, .6
Rectangle:
pos: self.pos
size: self.size
size_hint_y: None
height: my_label.height
Label:
id: my_label
text: root.text
font_size: '14dp'
text_size: (self.width, None)
size: self.texture_size
valign: 'top'
halign: 'left'
BoxLayout:
orientation: 'vertical'
size_hint_y: 1
canvas.before:
Color:
rgba: 0.9, .0, .5, .6
Rectangle:
pos: self.pos
size: self.size
TextInput:
text: root.PLACEHOLDER_TEXT
on_text: root.text = self.text
text_size: self.size
auto_indent: True
Label:
size_hint_y: None
height: '50dp'
text: 'String length: ' + str(len(root.text))
"""
Builder.load_string( kv_string )
class Example (BoxLayout ):
PLACEHOLDER_TEXT = PLACEHOLDER_TEXT
text = StringProperty( )
def __init__(self, **kwargs):
super( Example, self).__init__(**kwargs)
class MyApp(App):
def build(self):
root = Example()
return root
MyApp().run()