I'm new to Kivy and still working out the best way to use it. Currently, I'm taking an example from their repo (textsize demo) and attempting to use layout concepts in a personal project.
At the end of the day, all I want is a "navbar" at the top of my screen and a content section below it. At the moment, I'm trying to accomplish this with a BoxLayout which includes a StackLayout (nav items) and a GridLayout (other content).
My Code Snippet:
<WindowWidget>:
BoxLayout:
orientation: 'vertical'
StackLayout:
Label:
text: 'Test1'
Label:
text: 'Test1.1'
Label:
text: 'Test2'
This code produces this: Kivy Screenshot.
I don't understand why label 1.1 is pushed down on top of label 2 rather than appearing beside label 1.
I added the following code taken from the kivy examples hoping the height setting would add some clarity, but I get the error below:
Code:
<StackLayout>:
size_hint_y: None
spacing: dp(6)
padding: dp(6), dp(4)
height: self.minimum_height
Error:
Warning, too much iteration done before the next frame. Check your
code, or increase the Clock.max_iteration attribute
This is caused by my inclusion of the "height: self.minimum_height" line. However, this works in the demo, so I'm not sure how I managed to screw it up. I'll include the example's kv code below for reference.
The original:
BoxLayout:
orientation: 'vertical'
HeadingLabel:
text: 'These modify all demonstration Labels'
StackLayout:
# Button is a subclass of Label and can be sized to text in the same way
Button:
text: 'Reset'
on_press: app.reset_words()
ToggleButton:
text: 'Shorten'
on_state:
app.shorten=self.state=='down'
ToggleButton:
text: 'max_lines=3'
on_state:
app.max_lines=3 if self.state=='down' else 0
Spinner:
text: 'bottom'
values: 'bottom', 'middle', 'top'
on_text: app.valign=self.text
Spinner:
text: 'left'
values: 'left', 'center', 'right', 'justify'
on_text: app.halign=self.text
GridLayout:
id: grid_layout
cols: 2
height: cm(6)
size_hint_y: None
HeadingLabel:
text: "Default, no text_size set"
HeadingLabel:
text: 'text_size bound to size'
DemoLabel:
id: left_content
disabled_color: 0, 0, 0, 0
DemoLabel:
id: right_content
text_size: self.size
padding: dp(6), dp(6)
ToggleButton:
text: 'Disable left'
on_state:
left_content.disabled=self.state=='down'
# Need one Widget without size_hint_y: None, so that BoxLayout fills
# available space.
HeadingLabel:
text: 'text_size width set, size bound to texture_size'
text_size: self.size
size_hint_y: 1
DemoLabel:
id: bottom_content
# This Label wraps and expands its height to fit the text because
# only text_size width is set and the Label size binds to texture_size.
text_size: self.width, None
size: self.texture_size
padding: mm(4), mm(4)
size_hint_y: None
# The column heading labels have their width set by the parent,
# but determine their height from the text.
<HeadingLabel#Label>:
bold: True
padding: dp(6), dp(4)
valign: 'bottom'
height: self.texture_size[1]
text_size: self.width, None
size_hint_y: None
<ToggleButton,Button>:
padding: dp(10), dp(8)
size_hint: None, None
size: self.texture_size
# This inherits Button and the modifications above, so reset size
<Spinner>:
size: sp(68), self.texture_size[1]
<DemoLabel#Label>:
halign: app.halign
valign: app.valign
shorten: app.shorten
max_lines: app.max_lines
canvas:
Color:
rgb: 68/255.0, 164/255.0, 201/255.0
Line:
rectangle: self.x, self.y, self.width, self.height
<StackLayout>:
size_hint_y: None
spacing: dp(6)
padding: dp(6), dp(4)
height: self.minimum_height
TYIA
Related
I've created an app with a main page that is going to have multiple sections with recycleView data. I have added the first but when I try to add a second (eventually adding 4 to replace the APPS, OTHER, etc sections) the second recycleView shows up on top of the first one (see top left of image) instead of next to it like I would expect. I tried putting both of the recycleView widgets inside a boxlayout but the second RV still overlays the top.
The information should be filling the second box on the right here instead of overlaying on the far top left
Screen:
MDNavigationLayout:
ScreenManager:
id: screenManager
Screen:
name: "monitor"
CoverImage:
source: 'images/monitor.png'
# Darken the photo
canvas:
Color:
rgba: 0, 0, 0, .6
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: 'Administration'
left_action_items: [["menu", lambda x: nav_drawer.set_state('toggle')]]
elevation:5
BoxLayout:
ServerListWidget:
MountListWidget:
Please let me know if you need to see any additional code. I've also tried using a GridLayout with 2 cols
<MountListWidget>:
id: mountListWidget
recycleView: recycleView_mounts
BoxLayout:
padding: dp(20)
spacing: dp(10)
BoxLayout:
spacing: dp(10)
BoxLayout:
canvas.before:
Color:
rgba: 1,1,1,.1
# Use a float layout to round the corners
RoundedRectangle:
pos: self.pos
size: self.size
RecycleView:
id: recycleView_mounts
viewclass: 'MountWidget'
RecycleGridLayout:
cols: 1
default_size: self.parent.width, dp(60)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
width: self.minimum_width
spacing: dp(155), dp(0)
<ServerListWidget>:
id: serverListWidget
recycleView: recycleView
BoxLayout:
padding: dp(20)
spacing: dp(10)
BoxLayout:
orientation: "horizontal"
BoxLayout:
canvas.before:
Color:
rgba: 1,1,1,.1
# Use a float layout to round the corners
RoundedRectangle:
pos: self.pos
size: self.size
RecycleView:
id: recycleView
viewclass: 'ServerWidget'
RecycleGridLayout:
cols: 1
default_size: self.parent.width / 5, dp(60)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
width: self.minimum_width
spacing: dp(155), dp(0)
<CoverImage#CoverBehavior+Image>:
reference_size: self.texture_size
<MountWidget>:
BoxLayout:
#size_hint_max_x: dp(360)
size_hint_min_x: dp(150)
orientation: "horizontal"
BoxLayout:
orientation: "horizontal"
Button:
# Use the on_press to print troubleshooting info, otherwise comment out
on_press: print(self.background_color)
padding: dp(10), dp(10)
text_size: self.size
font_size: dp(18)
background_color: .5,1,1,.6
halign: "left"
valign: "top"
size: 1,1
text: str(root.name)
bold: True
color: (1,1,1)
<ServerWidget>:
BoxLayout:
#size_hint_max_x: dp(360)
size_hint_min_x: dp(150)
orientation: "horizontal"
BoxLayout:
orientation: "horizontal"
Button:
# Use the on_press to print troubleshooting info, otherwise comment out
on_press: print(self.background_color)
padding: dp(10), dp(10)
text_size: self.size
font_size: dp(22) if root.has_issue else dp(18)
background_color: .5,1,1,.6 #utils.get_random_color(alpha=.6)
halign: "left"
valign: "top"
size: 1,1
# Show last 4 of server name
text: str(root.name).upper()[-4:]
bold: True
color: utils.get_color_from_hex(warn) if root.has_issue else (1,1,1)
Button:
background_color: .5,1,1,.6
text_size: self.size
font_size: dp(22) if root.work >= 85 and root.work < 90 else dp(26) if root.work >= 90 else dp(18)
valign: "center"
halign: "center"
text: str(root.work)
padding: dp(8), dp(8)
bold: True if root.work > 90 else False
# yellow red white
color: (1,1,0) if root.work >= 85 and root.work < 90 else utils.get_color_from_hex(warn) if root.work >= 90 else (1,1,1)
<Builder_Screen>
ScrollView:
do_scroll_y:True
FloatLayout:
Button:
text:"Heading"
size_hint_x: .25
size_hint_y: .25
text_size: self.size
halign:"center"
valign:"center"
pos: 0,10
Button:
text:"Paragraph"
halign:"center"
valign:"center"
size_hint_x: .25
size_hint_y: .25
pos: (0,self.height)
I have some buttons like these with positions like pos: 0, self.height2 , self.height3 etc.
But, the scroll layout does not work as intended.
Can you help me regarding that!...
According to the documentation, regarding the child of a ScrollView:
By default, the size_hint is (1, 1), so the content size will fit your
ScrollView exactly (you will have nothing to scroll). You must
deactivate at least one of the size_hint instructions (x or y) of the
child to enable scrolling.
So, you probably need to set size_hint_y: None for your FloatLayout. That will cause further problems, because you have size_hint_y values for your Buttons. You can set the heights of your Buttons and eliminate the size_hint_y values. Another approach would be to use a GridLayout:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
kv = '''
<Builder_Screen>
ScrollView:
do_scroll_y:True
effect_cls: 'ScrollEffect'
GridLayout:
cols: 2
size_hint_y: None
height: self.minimum_height
padding: 5
spacing: 5
Button:
text:"Heading1"
halign:"center"
valign:"center"
size_hint_y: None
height: 48
Button:
text:"Paragraph1"
halign:"center"
valign:"center"
size_hint_y: None
height: 48
Button:
text:"Heading2"
halign:"center"
valign:"center"
size_hint_y: None
height: 48
Button:
text:"Paragraph2"
halign:"center"
valign:"center"
size_hint_y: None
height: 48
Button:
text:"Heading3"
halign:"center"
valign:"center"
size_hint_y: None
height: 48
Button:
text:"Paragraph3"
halign:"center"
valign:"center"
size_hint_y: None
height: 48
'''
class Builder_Screen(Screen):
pass
class TestApp(App):
def build(self):
Builder.load_string(kv)
return Builder_Screen()
TestApp().run()
Note that every Button has an explicit height. That is required if you use height: self.minimum_height for their container.
I have this kivy file here:
<Help>:
Label:
text: "[b]Help Page[/b]\n"
font_size: 30
markup: True
valign: "top"
color: 0,0,0,1
Label:
text: "[b] How To Use:[/b]\n"
font_size: 30
markup: True
valign: 'top'
color: 0,0,0,1
However, the text does not go to the top of the page. This is the output:
What is wrong with this? and may I also ask about how to format those overlapping text. Thanks :)
edit:
This is what I want to happen with the text:
desired output
So to get that result I tried using the valign and halign to format the text but it does not seem to work. Hope this clarified my question :)
Display Text at Top
To display the text at top using valign: 'top', replace text_size: root.width, None with text_size: root.width, root.height
Snippet
<Help>:
Label:
id: help
text_size: root.width, root.height
markup: True
valign: 'top'
halign: 'center'
color: 0,0,0,1
Output
Display Both Strings as One
To display both strings, "How to Operate: Some text here" and "Some title Some more text" as one, we will do the following:
kv file
<Help>:
Label:
id: help
text_size: root.width, None
markup: True
halign: 'center'
color: 0,0,0,1
Python Code
class Help(Screen):
def on_pre_enter(self, *args):
self.ids.help.text = "[size=30][b]How to Operate[/b][/size]\nSome text here" + \
"\n\n[size=30][b]Some title[/b][/size]\nSome more text"
Output - Combined Text
Text Overlapping
The Label's text are overlapping because you are adding two Label widgets on-top each other in a Screen Layout.
Add a BoxLayout as parent of the two Label widgets to prevent text overlapping.
Text Wrapping
Wraps the text at a certain width, provide the width. For example, a Label to be created in a box with width=200 and unlimited height.
Label(text='Very big big line', text_size=(200, None))
Snippet
<Help>:
BoxLayout:
orientation: 'vertical'
Label:
text_size: dp(230), None
height: self.texture_size[1]
text: "[size=30][b]How to Operate[/b][/size]Some text here"
markup: True
valign: "top"
halign: 'center'
color: 0,0,0,1
Label:
text_size: dp(150), None
text: "[size=30][b]Some title[/b][/size]Some more text"
markup: True
valign: 'top'
halign: 'center'
color: 0,0,0,1
Output
Text Alignment
Add text_size: self.size
Snippet
<Help>:
Label:
text_size: self.size
text: "[b]Help Page[/b]\n"
font_size: 30
markup: True
valign: "top"
color: 0,0,0,1
Label:
text_size: self.size
text: "[b] How To Use:[/b]\n"
font_size: 30
markup: True
valign: 'top'
color: 0,0,0,1
Text alignment and wrapping
In order for the halign and valign alignment properties to
take effect, set the text_size, which specifies the size of the
bounding box within which text is aligned.
Output
I created a custom button as follows
#: import icon kivy.garden.iconfonts.icon
<custom_Button#Button>:
background_normal: 'icons/backg.png'
RelativeLayout:
size: self.parent.width, self.parent.height
top: self.parent.top
right: self.parent.right
Label:
canvas.before:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
source: 'icons/restaurant.png'
color: 0,0,0,1
id: icon_name
markup: True
font_size: '20dp'
text: '{}'.format(icon('ion-settings', 38))
pos_hint: {'center_y': .5, 'right': .25}
size_hint: .18, .9
Label:
text:'Change Settings'
id: label
color: 0,0,0,1
text_size: self.size
halign: 'left'
valign: 'middle'
font_size: '20dp'
pos_hint: {'center_y': .5, 'right': 1}
size_hint: .7, .9
i want to be able to pass this (Custom_Button) as a child to different layouts, and change some of the attributes to what i want. Heres what i mean.
for example,
GridLayout:
custom_Button
custom_Button
custom_Button
But i would like to be able to explicitly change the label icon for the first label in the custom_Button, also the text in the second Label, so that for the three instances of the cutom_Button a different icon and text would be displayed. I really dont know how to achieve this. So please i need some help.
An example code will be very helpful. Thanks in advance...
First of all, do change the class name to something standard, like CustomButton. Then, define a new property containing the icon_source:
<CustomButton#Button>:
icon_source: 'icons/restaurant.png'
...
and refer to it later:
Rectangle:
pos: self.pos
size: self.size
source: root.icon_source
Then it's simple to change this for every instance, either in kv or in python:
GridLayout:
CustomButton:
icon_source: 'something/else.png'
I want to create a Widget changing only the text of a label inside it, but all the ways I find how to change this are by changing it in the python code instead of reusing only Kivy objects.
So I have a widget like the following:
<AmiLabel#Label>
color: .1, .5, .8, 1
font_size: 16
<AmiTextInput#TextInput>
font_size: 16
<PropertyInputForm>:
BoxLayout:
size: root.size
pos: root.pos
orientation: 'horizontal'
AmiLabel:
text: 'Folder Location'
size_hint_x: .5
AmiTextInput:
text: 'None'
size_hint_x: .5
<MainFormWidget>:
BoxLayout:
size: root.size
pos: root.pos
id: foo_bar
padding: 5
spacing: 5
canvas:
Color:
rgb: (1, 1, 1)
Rectangle:
pos: self.pos
size: self.size
orientation: 'vertical'
AmiLabel:
height: 36
size_hint_x: 1
size_hint_y: None
text: 'Project Name'
PropertyInputForm:
height: 36
size_hint_x: 1
size_hint_y: None
# I WANT TO CHANGE THE TEXT OF THE LABEL IN HERE
PropertyInputForm:
height: 36
size_hint_x: 1
size_hint_y: None
# I WANT TO CHANGE THE TEXT OF THE LABEL IN HERE
All I want to is change the text of the label from another widget in another level without touching the python code.
¿Is that possible?
One simple way would be to add a new property to your PropertyInputForm and reference or set that.
<PropertyInputForm>:
new_text_property: ''
BoxLayout:
size: root.size
pos: root.pos
orientation: 'horizontal'
AmiLabel:
text: root.new_text_property
size_hint_x: .5
AmiTextInput:
text: 'None'
size_hint_x: .5
and later
PropertyInputForm:
height: 36
size_hint_x: 1
size_hint_y: None
new_text_property: 'whatever'
You may also need to declare new_text_property in the python class to have it be a StringProperty rather than an ObjectProperty, though even that is I think not necessary in kivy 1.8.