Kivy .kv file global variables puzzle - kivy

I declared some color constants as global variables in .kv file. They work in certain situations but not in others. An example ColorConstants.kv is
#:kivy 2.0.0
#:set BLUE (0, 0, 1, 1)
#:set WHITE (1, 1, 1, 1)
<Header#BoxLayout>:
# color constants don't work here: NoneType errors
# font_color: WHITE
# header_color: BLUE
# need to use numeric lists for font_color, header_color
font_color: (1, 1, 1, 1)
header_color: (0, 0, 1, 1)
header_text: ""
size_hint: 1, None
height: dp(50)
canvas.before:
Color:
rgba: self.header_color
Rectangle:
size: self.size
pos: self.pos
Label:
color: root.font_color
text: root.header_text
bold: True
BoxLayout:
orientation: "vertical"
Header:
header_text: "My Header"
Label:
# color constants work here, no errors
color: BLUE
text: "This is blue text on white"
canvas.before:
Color:
rgba: WHITE
Rectangle:
size: self.size
pos: self.pos
Accompanying ColorConstants.py:
from kivy.app import App
class ColorConstantsApp(App):
pass
ColorConstantsApp().run()
I am confused why I can use BLUE and WHITE in Label but not in Header. If I use WHITE and BLUE for font_color and header_color instead of the (1,1,1,1) and (0,0,1,1), I encounter a TypeError: 'NoneType' object is not iterable error.

It appears that the canvas instructions are being created before the header_color is assigned. You can work around that by changing:
rgba: self.header_color
to:
rgba: self.header_color if self.header_color else (0,1,0,1)
This just checks if header_color is None and uses something else in that case. Then, when header_color is assigned, the correct color is used.

Related

Kivy. Checkbox on top of background image

My goal is to set the checkbox on top of the image in below class. I'm struggling to find a layout in which I can position one layout above another. The only way I could think of at the moment is to show the image as the background image of RelativeLayout not as AsyncImage but I believe there is a clean way to achieve this.
Class
<FriendTile>
orientation: 'vertical'
size_hint: None, None
height: pic.height + username.height
width: pic.width
background_color: app.alert_color
active_color: app.main_bcolor
unactive_color: app.main_bcolor
canvas.before:
Color:
rgba: (0, 0, 1, 1) if self.background_color is None else self.background_color
Rectangle:
pos: self.pos
size: self.size
RelativeLayout:
id: pic
size_hint: None, None
size: 100, 100
CheckBox:
size_hint: .1, .1
pos_hint: {'top': 1, 'right': 1}
canvas.after:
Color:
rgba: (0, 1, 0, 1)
Rectangle:
pos: self.pos
size: self.size
AsyncImage:
id: image
size_hint: .9, .9
pos_hint: {'center_x': .5, 'center_y': .5}
source: root.avatar
canvas.before:
Color:
rgba: (0, 1, 1, 1) # if root.background_color is None else root.background_color
Rectangle:
pos: self.pos
size: self.size
Expectations
Reality

Error when making rounded corners for buttons in Kivy v2.0.0

I'm trying to make rounded corners for a button in a kv file like so:
test.kv
#:kivy 2.0
<CustomGridLayout>
GridLayout:
cols: 1
RoundedButton:
text: 'Button #1'
<RoundedButton#Button>
background_color: (51/255.0, 51/255.0, 51/255.0, 1)
background_normal: ''
background_down: ''
size_hint: None, None
size: 300, 50
canvas.before:
Color:
rgba: self.background_color
RoundedRectangle:
size: self.size
pos: self.pos
radius: 10,
test.py
import kivy
kivy.require('2.0.0')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.core.window import Window
class CustomGridLayout(Widget):
pass
class TestApp(App):
def build(self):
Window.size = (360,640)
Window.clearcolor = (238/255.0, 238/255.0, 238/255.0, 1)
return CustomGridLayout()
if __name__ == '__main__':
TestApp().run()
This does not result in rounded corners for me.
However when I add border: 0, in RoundedButton like this:
test.kv
<RoundedButton#Button>
...
border: 0,
canvas.before:
...
RoundedRectangle:
radius: 10,
The button is then rounded. But then upon starting the app, as well as every time I click the button, I get the following error:
ValueError: need more than 1 value to unpack
Exception ignored in: 'kivy.graphics.vertex_instructions.BorderImage.bui
ld'
This is referring to their being only a single value in border so I changed it to 4 values (i.e. 0, 0, 0, 0) and the error went away but corners are no longer rounded.
I also tried border: 10, 10, 10, 10 but again no rounding.
And lastly I've also tried using the border property with no canvas.before but to no avail.
You need to eliminate the background_color by making it transparent. The background_color is what you are seeing. By making it transparent, it allows you to see the rounded rectangle:
<RoundedButton#Button>
background_color: (0, 0, 0, 0)
background_normal: ''
background_down: ''
size_hint: None, None
size: 300, 50
canvas.before:
Color:
rgba: 51/255.0, 51/255.0, 51/255.0, 1
RoundedRectangle:
size: self.size
pos: self.pos
radius: 10,

How can I make border to the text in the label?

I'm still learning kivy language .
please can you tell me how to add a border to a text in a label in the kv file
and thanks
In the kivy language documentation, you can redefine a widget's style by adding a - to the beginning of the kv rule. So, in the kv you can define a new widget like this:
<-LabelWithBorder#Label>:
border_width: 0
border_color: [1,1,1,1]
canvas.before: # draw the border
Color:
rgba: root.border_color if root.border_width > 0 else [0,0,0,1]
Rectangle:
size: self.size
pos: self.pos
Color:
rgba: 0, 0, 0, 1
Rectangle:
size: self.width - 2*root.border_width, self.height - 2*root.border_width
pos: int(self.center_x - (self.width - 2*root.border_width)/2.), int(self.center_y - (self.height - 2*root.border_width)/2.)
canvas: # modified from Label
Color:
rgba: 1, 1, 1, 1
Rectangle:
texture: self.texture
size: self.texture_size[0] - 2*root.border_width, self.texture_size[1] - 2*root.border_width
pos: int(self.center_x - self.width/2.) + root.border_width, int(self.center_y - self.height/2.) + root.border_width
The canvas.before is the section that draws the border, and the canvas section is the normal Label style with slight modifications to account for the border.
This can be used, for example, like this:
FloatLayout:
LabelWithBorder:
text: 'Hello, World'
font_size: 50
border_width: 10
border_color: [1,0,0,1]
size_hint: None, None
size: self.texture_size
pos_hint: {'center_x':0.5, 'center_y':0.5}
I believe you are looking for 'outline'
This is the docs link for it Label Outline - Kivy Docs
Here's some example code (color defaults to black .. 0, 0, 0, 1):
Label:
id: label_StackOverflowSample
pos_hint: {"x": 0.25, "y": 0.18}
size_hint: 0.6, 0.365
font_size: 18
text_size: self.size
halign: 'left'
italic: True
outline_width: 10
outline_colour: (0, 0, 0, 1)
text:
'''
long long long
long long long
long long long
long long long
long long long
textt
'''
And this is a picture showing its example:
sorry, not enough reputation to post pictures yet
Hope this helps

Why doesn'y my Kivy label text fit in the label?

I have a big knowledge gap about how Kivy sizes the various elements, and it is being resistant to my attempts to fix it.
Here is my stopwatch.kv file
#:kivy 1.11.1
<StopWatch>:
BoxLayout:
orientation: 'vertical'
canvas.before:
Color:
rgba: .2, .2, .2, 1
Rectangle:
pos: self.pos
size: self.size
Label:
text: "A long piece of text #1"
size: self.texture_size
canvas.before:
Color:
rgba: .5, .1, .1, 1
Rectangle:
pos: self.pos
size: self.size
Label:
text: "A long piece of text #2"
size: self.texture_size
text_size: root.width, None
canvas.before:
Color:
rgba: .5, .5, .1, 1
Rectangle:
pos: self.pos
size: self.size
Here is main.py:
from kivy.app import App
from kivy.uix.widget import Widget
class StopWatch(Widget):
pass
class StopWatchApp(App):
def build(self):
sw = StopWatch()
return sw
if __name__ == '__main__':
StopWatchApp().run()
I have figured out the size of the window (on my desktop) is, by default, unrelated to its contents. Okay.
I have figured out the size of the BoxLayout is, by default, unrelated to the window size. Instead, it is large enough to enclose its children widgets. Okay.
I have figured out that the size of a Label is, by default, unrelated to the size of its text contents. Instead, I think it is 100x100, but I haven't found that documented.
If you want a label to be big enough to cover the text (and hence the BoxLayout to cover the text), you have to specify:
size: self.texture_size
Okay. But I do that for the first label, and it still exceeds its label size (which I use a rectangle coloured red to visualise.)
So, I try to specify the text_size in the second label - making it the full width of the window - but its text doesn't appear at all!
I know I am missing something obvious, but reading the manuals hasn't helped.
The problem is that your StopWatch extends Widget, and Widget is not intended as a container. See the Widget Class.
Note the part that says:
A Widget is not a Layout: it will not change the position or the size
of its children. If you want control over positioning or sizing, use
a Layout.
The default size of a widget is (100, 100). This is only changed if
the parent is a Layout. For example, if you add a Label inside a
Button, the label will not inherit the button’s size or position
because the button is not a Layout: it’s just another Widget.
The default size_hint is (1, 1). If the parent is a Layout, then the
widget size will be the parent layout’s size.
So, your StopWatch will be size (100,100), and your Labels are wider than that. A simple solution is to change your StopWatch to extend a Layout like this:
class StopWatch(BoxLayout):
pass
Then you can rewrite your kv as:
#:kivy 1.11.1
<StopWatch>:
orientation: 'vertical'
canvas.before:
Color:
rgba: .2, .2, .2, 1
Rectangle:
pos: self.pos
size: self.size
Label:
text: "A long piece of text #1"
size: self.texture_size
canvas.before:
Color:
rgba: .5, .1, .1, 1
Rectangle:
pos: self.pos
size: self.size
Label:
text: "A long piece of text #2"
size: self.texture_size
text_size: root.width, None
canvas.before:
Color:
rgba: .5, .5, .1, 1
Rectangle:
pos: self.pos
size: self.size
Note that the BoxLayout has been removed from the kv, since StopWatch is now a BoxLayout. Also, note that your size properties of the Labels in your kv will have no effect unless you set size_hint to (None, None), since size_hint takes precedence over size.

Changing the icon of Kivy FileChooserIconView

I am looking for a way to change the file icon of FileChooserIconView and set it programatically. I am looking at Kivy's filechooser.py source code but cannot find where the icon is being set.
Currently, the default background color of FileChooserIconView is black and the current icon works well with that background. I need to change my app's background to white, and the current icon doesn't looks nice with white background and I also have a specific file icon that needs to be used for the white background.
The file icon is defined in the style.kv file of your Kivy installation. And it is referenced in the FileChooserIconView and the FileChooserIconLayout classes as:
_ENTRY_TEMPLATE = 'FileIconEntry'
You can redefine that template using something like:
Builder.load_string('''
[FileIconEntry#Widget]:
locked: False
path: ctx.path
selected: self.path in ctx.controller().selection
size_hint: None, None
on_touch_down: self.collide_point(*args[1].pos) and ctx.controller().entry_touched(self, args[1])
on_touch_up: self.collide_point(*args[1].pos) and ctx.controller().entry_released(self, args[1])
size: '100dp', '100dp'
canvas:
Color:
rgba: 1, 1, 1, 1 if self.selected else 0
BorderImage:
border: 8, 8, 8, 8
pos: root.pos
size: root.size
source: 'atlas://data/images/defaulttheme/filechooser_selected'
Image:
size: '48dp', '48dp'
source: 'atlas://data/images/defaulttheme/filechooser_%s' % ('folder' if ctx.isdir else 'file')
pos: root.x + dp(24), root.y + dp(40)
Label:
text: ctx.name
text_size: (root.width, self.height)
halign: 'center'
shorten: True
size: '100dp', '16dp'
pos: root.x, root.y + dp(16)
Label:
text: '{}'.format(ctx.get_nice_size())
font_size: '11sp'
color: .8, .8, .8, 1
size: '100dp', '16sp'
pos: root.pos
halign: 'center'
''')
The above code just duplicates the template definition from style.kv, but you can make any changes to the above template, and those changes will affect the FileChooserIconView. The Image is the actual icon.

Resources