How to put a Lable on a Rectangle in Kivy - python-3.x

I am quite a novice programming with kivy, and I just wanted to put a green rectangle under a label to make it look a little bit like a display. I've tried making a label and then, inside of it, a canvas with the rectangle, but when it's time to put them together, the self.pos doesn't put them on the same place... Here's part of the code:
'''.kv file'''
Label:
id: s_label
size:50,50
text:'[font=Digital-7][color=D20000][size=24] S= [/color][/font][/size]'
markup:True
pos_hint:{'x':0.3,'y':-0.2}
canvas:
canvas.before:
Color:
rgba: 0, 153, 0, 0.5
RoundedRectangle:
pos: self.pos[0] , self.pos[1]
size: (self.width/6, self.width/16)
id: S_rect
The Label is inside a FloatLayout, but I don't know if that is relevant.
this is what I get. See, the green rectangle is far away from the red S label

Two problems with your code:
You must include size_hint: None, None in your Label in the kv. Without that, the size has no effect, and the Label fills its parent.
The pos_hint:{'x':0.3,'y':-0.2} positions the Label below the bottom of the parent. Try something like pos_hint:{'x':0.3,'y':0.2}.

Related

Python Kivy Widgets

I want to add two widgets or maybe three to form a page that show me Message and Take my input message and on pressing send button it pass that message to the send_msg() function Something like in Image:
I know the way to do it in py file but I need code for kv file
Thank You in Advance for your help
Here is The Code:
class ChatPage(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# We are going to use 1 column and 2 rows
self.cols = 1
self.rows = 2
# First row is going to be occupied by our scrollable label
# We want it be take 90% of app height
self.history = Label(height=Window.size[1]*0.9, size_hint_y=None)
self.add_widget(self.history)
# In the second row, we want to have input fields and Send button
# Input field should take 80% of window width
# We also want to bind button click to send_message method
self.new_message = TextInput(width=Window.size[0]*0.8, size_hint_x=None, multiline=False)
self.send = Button(text="Send")
self.send.bind(on_press=self.send_message)
# To be able to add 2 widgets into a layout with just one column, we use additional layout,
# add widgets there, then add this layout to main layout as second row
bottom_line = GridLayout(cols=2)
bottom_line.add_widget(self.new_message)
bottom_line.add_widget(self.send)
self.add_widget(bottom_line)
# Gets called when either Send button or Enter key is being pressed
# (kivy passes button object here as well, but we don;t care about it)
def send_message(self, _):
print("send a message!!!")
It is difficult to get a widget to take up two columns in a GridLayout. A simpler approach would b to use BoxLayouts:
BoxLayout:
orientation: 'vertical'
ScrollView:
size_hint_y: 0.9
Label:
id: label
size_hint_y: None
height: self.texture_size[1]
BoxLayout:
orientation: 'horizontal'
size_hint_y: 0.1
TextInput:
id: ti
size_hint_x: 0.8
Button:
text: 'send'
size_hint_x: 0.2

Correctly use Kivy Canvas in Python

Assuming I define a BoxLayout but want to add, say a blue background to it, in Kivy it would look something like this:
BoxLayout:
canvas.before:
Color:
rgb: 0, 0, 1
Rectangle:
size: self.size
pos: self.pos
I tried to do this in Python like this:
box = BoxLayout()
with box.canvas.before:
Color(rgb=(0, 0, 1))
Rectangle(size=box.size, pos=box.pos)
This does draw a rectangle, but not in the correct size or position. My question is: is there a way to create a Rectangle (or another way to add a background to a BoxLayout) the same size and position of the defined BoxLayout? The closest I have ever gotten is physically setting the size and shape, but I would like this Rectangle to be dynamically resizable so that I don't have too many values hard-coded. Thanks in advance!
Typically, that code would appear in some method of a class, so a reference to the Rectangle could be saved as, for example, self.bg. So it would like something like this:
class MyBackground(FloatLayout):
def __init__(self, **kwargs):
super(MyBackground, self).__init__(**kwargs)
self.box = BoxLayout(size_hint=(0.5, 0.5))
with self.box.canvas.before:
Color(rgba=(1, 0, 0, 1))
self.bg = Rectangle(pos=self.pos, size=self.size)
# bindings to keep size and position of the Rectangle up to date
self.box.bind(pos=self.update_bg)
self.box.bind(size=self.update_bg)
# add box to this layout
self.add_widget(self.box)
def update_bg(self, *args):
self.bg.pos = self.box.pos
self.bg.size = self.box.size

bind, text of label in kv file to a method in python file

I want to bind a function in .kv file to a method in .py file in simple words I want to update quotes every day on the main screen of my app so I used screen widget to display the quotes so how do I bind the label.text to a method in .py file
here's the class of .py file I want to bind
class MainScreen(FloatLayout):
def quote(self):
self.quote.text = 'I often think that the night is more alive and more richly colored than
the day. - Vincent van Gogh'
here's the root widget of .kv file
<MainScreen>:
story: story
canvas:
Color:
rgba: 0, 0, 0, 1
Rectangle:
pos: self.pos
size: self.size
Label:
id: story
text: root.quote
font_size: '20sp'
size: self.texture_size
pos_hint: {'x': -0.2, 'y': 0.27}
but it showing error saying label.text must be string
You can do this using a kivy Property, and using the quote() method to update that Property. Here is a way to do that in your code:
class MainScreen(FloatLayout):
quote_text = StringProperty('Not Set')
def quote(self, *args):
self.quote_text = 'I often think that the night is more alive and more richly colored than the day. - Vincent van Gogh'
The quote_text is a StringProperty that can be referenced in the kv and the quote method now updates that StringProperty.
And in your kv:
<MainScreen>:
story: story
canvas:
Color:
rgba: 0, 0, 0, 1
Rectangle:
pos: self.pos
size: self.size
Label:
id: story
text: root.quote_text
font_size: '20sp'
size: self.texture_size
pos_hint: {'x': -0.2, 'y': 0.27}
Then, calling the quote() method will update the Label text. To test it, you can use a build() method of your App as:
def build(self):
main = MainScreen()
Clock.schedule_once(main.quote, 5)
return main

KIVY - Changing color of a canvas from Python

After many trial and research I did not manage to accomplish what I want to do.
I have a kivy file (lets say test.kv) in which I have the following section:
BoxLayout:
id: WebcamSection
orientation: "vertical"
canvas:
Color:
rgb: (0.87451, 0.294118, 0.266667, 1)
Rectangle:
pos: self.pos
size: self.size
KivyCamera:
allow_stretch: True
keep_ratio: True
id: pbyCam
From my .py I would like to be able to modify the color of that Canvas to (1,1,1,1) but so far I did not find the solution.
I am currently changing the color for many other elements such as buttons, labels... without any issues
Any suggestion about how I should handle that?
Thanks a lot for you help
Ok I finally got the answer after additional trials :)
Here is the change I have made to my .kv file:
BoxLayout:
id: WebcamSection
orientation: "vertical"
test_color: (0.87451, 0.294118, 0.266667, 1)
canvas:
Color:
rgb: self.test_color
Rectangle:
pos: self.pos
size: self.size
So in the end it was very easy, just need to add a variable (in this case I called it test_color) and refer to it in order to set the color of the canvas.
Then in my .py I can call the id of the BoxLayout:
self.WebcamSection = self.ids['WebcamSection']
And to finish I have a function to change the color with the below line:
self.WebcamSection.test_color = (1,1,1,1)

Kivy different type of coordinates

I try to under stand the different types of coordinates:
Global,
Local,
Window and
Widget
coordinates.
using the program:
class TargetUI(BoxLayout):
js_type = NumericProperty(0)
def __init__(self, **arg):
super(TargetUI, self).__init__(**arg)
btn1 = Button(text='Hello world ' + str(self.js_type))
self.add_widget(btn1)
def on_touch_up(self, touch):
# here, you don't check if the touch collides or things like that.
# you just need to check if it's a grabbed touch event
Logger.info("in touch up")
Logger.info("global coordinates: " + str(touch.pos))
if self.collide_point(*touch.pos):
touch.push()
# if the touch collides with our widget, let's grab it
touch.grab(self)
Logger.info("In widget " + str(self.js_type))
touch.apply_transform_2d(self.to_local)
Logger.info("Local coordinates " + str(touch.pos))
touch.apply_transform_2d(self.to_window)
Logger.info("Windows coordinates " + str(touch.pos))
touch.apply_transform_2d(self.to_widget)
Logger.info("Widget coordinates " + str(touch.pos))
touch.ungrab(self)
# and accept the touch.
return True
class CombWidget(Widget):
pass
class MyPaintApp(App):
def build(self):
return CombWidget()
if __name__ == '__main__':
MyPaintApp().run()
and
#:kivy 1.7.1
<CombWidget>:
tg1: tg1
tg2: tg2
tg3: tg3
BoxLayout:
size: root.size
orientation: 'vertical'
padding: 20
TargetUI:
js_type: 1
id: tg1
TargetUI:
js_type: 2
id: tg2
TargetUI:
id: tg3
js_type: 3
All the coordinates written out by on_touch_up is the same, but expected some difference. Why are are all the coordinates the same?
I also expected to see the Button text to end with 1,2 or 3 but their are all 1. How can I make the Button text be depended in self.js_type?
These are useful when there are coordinate changes. For example, with the scatter widget, here is an example where one of the widgets is put in a Scatter and you can move it (somehow it gets back in place when you click it again, but it's convenient). When you do that, you should see that the coordinates are no longer the same. understanding the difference between them is left as an exercise to the reader :)
from kivy.base import runTouchApp
from kivy.lang import Builder
kv = '''
GridLayout:
cols: 2
spacing: 10
ClickBox
Scatter:
ClickBox:
pos: 0, 0
size: self.parent.size
Widget:
ClickBox:
pos: self.parent.pos
size: self.parent.size
<ClickBox#Widget>:
canvas:
Rectangle:
pos: self.pos
size: self.size
on_touch_move:
if self.collide_point(*args[1].pos): print self.to_window(*args[1].pos)
if self.collide_point(*args[1].pos): print self.to_parent(*args[1].pos)
if self.collide_point(*args[1].pos): print self.to_widget(*args[1].pos)
if self.collide_point(*args[1].pos): print self.to_local(*args[1].pos)
'''
if __name__ == '__main__':
runTouchApp(Builder.load_string(kv))
The Documentation for RelativeLayout clarified this issue for me.
Parent coordinates
Other RelativeLayout type widgets are Scatter, ScatterLayout, and
ScrollView. If such a special widget is in the parent stack, only then
does the parent and local coordinate system diverge from the window
coordinate system. For each such widget in the stack, a coordinate
system with (0, 0) of that coordinate system being at the bottom left
corner of that widget is created. Position and touch coordinates
received and read by a widget are in the coordinate system of the most
recent special widget in its parent stack (not including itself) or in
window coordinates if there are none. We
call these coordinates parent coordinates.
So you must use one of the above special widgets for local coordinates to diverge from window coordinates. Tshirtman's answer works because he used the scatter widget which is one of the special widgets.

Resources