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.
Related
I am trying to create an App in Kivy along with Python that contains a long scrollable list of text. Inside of this list would be Labels some of which are just headers and some of which contain the text body. My question is, is there a way to have a selectable portion of the text in a Label that on press would set the screen focus to a desired other Label? I have found ways on how to hyperlink specific text to a webpage but not a great way to set the screen focus inside of the same app instead.
i.e.:
<SecondWindow#ScrollView>
name:"Page"
canvas.before:
Color:
rgba:(1,1,1,1)
Rectangle:
pos: self.pos
size: self.size
ScrollView:
GridLayout:
cols:1
size_hint_y:None
size_hint:1,None
height:self.minimum_height
Label:
name:"PrefaceL1"
canvas.before:
Color:
rgba:(109/255.0,114/255.0,219/255.0,1)
Rectangle:
pos:self.pos
size:self.size
size_hint_y: None
height: self.texture_size[1]
text_size:self.width,None
text:' Preface'
font_size:'52sp'
Label:
name:"PrefaceB1"
canvas.before:
Color:
rgba:(1,1,1,1)
Rectangle:
pos:self.pos
size:self.size
size_hint_y: None
height: self.texture_size[1]
text_size:self.width,None
text:"Long [KEY WORD SOMEWHERE IN HERE] Text" <--------
color: 0,0,0,1
font_size:'16sp'
Label:
name:"PrefaceL2" <--------- Location of desired screen focus.
canvas.before:
Color:
rgba:(0,0,0,1)
Rectangle:
pos:self.pos
size:self.size
size_hint_y: None
height: self.texture_size[1]
text_size:self.width,None
text:' Preface'
font_size:'52sp'
font_size:'16sp'
According to the Kivy docs
https://kivy.org/doc/stable/api-kivy.uix.scrollview.html#kivy.uix.scrollview.ScrollView.scroll_y
You can focus on the Label you want by do_scroll_y to the value of desired_label.y / layout_of_the_scroll_view.height
I have a boxlayout (1) of two labels placed next to each other. I use this widget to build to another boxlayout containing a list of the widgets of (1).
For readability, I like to have each line separated by a thin line and I also like to have a vertical line to separate two labels.
something similar to:
Label 1 content
label 2 content
Label 1 content 1
label 2 content 2
:----------------:
:-----------------:
etc. And alternate the back ground color of each line.
Could I get some help in how to do this, please.
Many thanks.
You can define a background color, then define Labels that obscure that background color except for a small area around the Labels. Here is a kv file that does that:
BoxLayout:
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
spacing: 2
padding: 2
# define background that will appear as outlines
canvas.before:
Color:
rgba: 1,0,0,1 # outline color
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
spacing: 2
size_hint_y: None
height: self.minimum_height
LabelNotTransparent:
text: 'Label 1 content'
size_hint_y: None
height: self.texture_size[1]
LabelNotTransparent:
text: 'Label 2 content'
size_hint_y: None
height: self.texture_size[1]
BoxLayout:
spacing: 2
size_hint_y: None
height: self.minimum_height
LabelNotTransparent:
text: 'Label 1 content 1'
size_hint_y: None
height: self.texture_size[1]
LabelNotTransparent:
text: 'Label 2 content 2'
size_hint_y: None
height: self.texture_size[1]
BoxLayout:
spacing: 2
size_hint_y: None
height: self.minimum_height
LabelNotTransparent:
text: ':----------------:'
size_hint_y: None
height: self.texture_size[1]
LabelNotTransparent:
text: ':----------------:'
size_hint_y: None
height: self.texture_size[1]
<LabelNotTransparent#Label>:
# normal Labels are transparent, this makes it not transparent
canvas.before:
Color:
rgba: 0,0,0,1
Rectangle:
pos: self.pos
size: self.size
The spacing and padding values are the areas that will show through and appear as outlines.
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.
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
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.