Kivy align Switch left in BoxLayout - python

I want to align a switch to the left side of a BoxLayout. For labels i achieved this with the following code:
text_size: self.size
This places my label text on the bottom-left corner of my boxlayout. However, i cant manage to do the same with a switch widget. I tried playing around with size_hint_x, size, pos and so on, but i cant align the elements properly without disturbing the sizes of the boxes.
Currently my labels are aligned properly, so i tried assigning ids to them and orientating the switch according to the current pos of the label with the following:
BoxLayout:
padding: 100, 0, 0, 0
orientation: 'horizontal'
text_size: self.size
valign: 'middle'
Label:
text: 'this is already correctly aligned'
id: 'labelCorrectlyAligned'
#Some other code
BoxLayout:
padding: 100, 0, 0, 0
orientation: 'horizontal'
#here i need something like text_size: self.size but for switches
Switch:
size_hint_x: labelCorrectlyAligned.pos[0] #this should be the current X-position of the label
#pos_hint_x: labelCorrectlyAligned.pos[0] #didnt work either

If I understand your question correctly, you can just set the size of the Switch to its minimum as noted in the documentation:
The minimum size required is 83x32 pixels
Any size bigger than that will just have the 83 by 32 image centered in that larger size.
Also, in these layout situations, I find it helpful to color the backgrounds of Widgets to easily see exactly where they are and how big they are. Here is a modified version of your 'kv` that does both of the above suggestions:
BoxLayout:
orientation: 'vertical'
BoxLayout:
padding: 100, 0, 0, 0
orientation: 'horizontal'
canvas.before:
Color:
rgba: 1,0,0,1
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'this is already correctly aligned'
text_size: self.size
valign: 'middle'
id: 'labelCorrectlyAligned'
canvas.before:
Color:
rgba: 0,1,0,1
Rectangle:
pos: self.pos
size: self.size
# Some other code
BoxLayout:
padding: 100, 0, 0, 0
orientation: 'horizontal'
canvas.before:
Color:
rgba: 1,1,0,1
Rectangle:
pos: self.pos
size: self.size
Switch:
size_hint: None, None
size: 83, 32
on_active: app.setBluetoothConnection(self)
canvas.before:
Color:
rgba: 0,0,1,1
Rectangle:
pos: self.pos
size: self.size
Of course, when you are satisfied with the layout, just remove the canvas.before: blocks.
By the way, the text_size and valign have no effect in the BoxLayout, they must be in the Label rule.
So, both the Switch and the Label are on the left of the BoxLayout. A common way to size a Label is to use:
size_hint: None, None
size: self.texture_size
This gives you the minimum size for a Label.

Edit: For convenience, i created a dynamic class for my left-aligned switch:
<SwitchL#Switch>:
size_hint_x: None
size: 83, self.height

Related

Is it possible to add padding and margin to each widgets in kivy?

I want to apply margin and padding to each widgets is that possible in kivy?
Here is the exact image I need; I want to apply padding and margin to each button:
![1][1]
I applied one method in kivy, put the widget in 2 layouts but the problem is without adding margin, padding there is space between the widgets. How can we set the 2 layout size according to widget size? Here is my code:
kv file
<Demoproject>:
Screen:
name:"screen_2917"
canvas.before:
Color:
rgba:(1.0,1.0,1.0,1.0)
Rectangle:
pos:self.pos
size:self.size
#source:""
BoxLayout:
orientation:"vertical"
BoxLayout:
orientation:"vertical"
padding:dp(0),dp(0),dp(0),dp(0)
color:(1,0,0,1)
canvas:
Color:
rgb: [.10, .10, .10]
Rectangle:
pos: self.pos
size: self.size
GridLayout:
cols:1
padding:dp(0),dp(0),dp(0),dp(0)
canvas:
Color:
rgb: [.6, .6, .6]
Rectangle:
pos: self.pos
size: self.size
Button:
text:"close"
size:(200,100)
size_hint:(None,None)
BoxLayout:
orientation:"vertical"
padding:dp(0),dp(0),dp(0),dp(0)
color:(1,0,0,1)
canvas:
Color:
rgb: [.10, .10, .10]
Rectangle:
pos: self.pos
size: self.size
GridLayout:
cols:1
padding:dp(0),dp(0),dp(0),dp(0)
canvas:
Color:
rgb: [.6, .6, .6]
Rectangle:
pos:self.pos
size:self.size
Button:
text:"close"
size:(200,100)
size_hint:(None,None)
I think you can simplify your kv file and just use padding and spacing. Like this:
<Demoproject>:
Screen:
name:"screen_2917"
canvas.before:
Color:
rgba:(1.0,1.0,1.0,1.0)
Rectangle:
pos:self.pos
size:self.size
#source:""
BoxLayout:
orientation:"vertical"
size_hint: None, None
size: self.minimum_size
pos_hint: {'top': 1}
padding: 5 # between Boxlayout edges and Buttons
spacing: 5 # between Buttons
Button:
text:"close"
size:(200,100)
size_hint:(None,None)
Button:
text:"close"
size:(200,100)
size_hint:(None,None)
Padding can only be applied by a few of the Layout classes. So if you want different padding for each Button, you must use separate Layouts. Here is another version of your kv that uses AnchorLayout to apply different padding for each Button:
<Demoproject>:
Screen:
name:"screen_2917"
canvas.before:
Color:
rgba:(1.0,1.0,1.0,1.0)
Rectangle:
pos:self.pos
size:self.size
#source:""
BoxLayout:
orientation:"vertical"
size_hint: None, None
size: self.minimum_size
pos_hint: {'top': 1}
canvas.before:
Color:
rgba:(0,1,0,1)
Rectangle:
pos:self.pos
size:self.size
AnchorLayout:
size_hint: None, None
size: butt1.width + self.padding[0] + self.padding[2], butt1.height + self.padding[1] + self.padding[3]
padding: 20
canvas.before:
Color:
rgba: 1,0,0,1
Rectangle:
pos: self.pos
size: self.size
Button:
id: butt1
text:"close"
size:(200,100)
size_hint:(None,None)
AnchorLayout:
size_hint: None, None
size: butt2.width + self.padding[0] + self.padding[2], butt2.height + self.padding[1] + self.padding[3]
padding: 5, 10, 15, 20
canvas.before:
Color:
rgba: 0,0,1,1
Rectangle:
pos: self.pos
size: self.size
Button:
id: butt2
text:"close"
size:(200,100)
size_hint:(None,None)
The canvas instructions are just to more easily visualize what is happening. The AnchorLayout sizes are calculated to just fit the Button with the specified padding.

How to add multiple labels in .kv file

My problem is that I want to add multiple labels without having to repeat so many lines of code. I have searched for solutions for a long time and all I see is simply writing a for loop in the python file instead of working on the .kv file. However, the location of the labels I want to add is inside a GridLayout inside a scrollLayout inside a BoxLayout and inside another BoxLayout. Is the only solution really to code all of that in my python file? Is there a better approach to this solution?
This is my first time asking a question on StackOverflow, I am very new to all of this, please correct me if I haven't asked the question in a conventional or clear format. Thank you very much.
python code
from kivy.uix.widget import Widget
from kivy.app import App
from kivy.lang import Builder
Builder.load_file('widgetq.kv')
class Win(Widget):
pass
class WidgetApp(App):
def build(self):
return Win()
if __name__ == '__main__':
WidgetApp().run()
.kv file code
<Win>
box1:box1
BoxLayout:
size: root.size
orientation: "vertical"
BoxLayout:
size_hint: 1, 5
ScrollView:
GridLayout:
id:box1
orientation: 'tb-lr'
height: self.minimum_height
size_hint_y: None
row_default_height:180
spacing: 2
cols:1
Label:
background_color:(150/255, 150/255, 150/255, 1)
text:"table"
canvas.before:
Color:
rgba: self.background_color
Rectangle:
size: self.size
pos: self.pos
Label:
background_color:(150/255, 150/255, 150/255, 1)
text:"table"
canvas.before:
Color:
rgba: self.background_color
Rectangle:
size: self.size
pos: self.pos
Label:
background_color:(150/255, 150/255, 150/255, 1)
text:"table"
canvas.before:
Color:
rgba: self.background_color
Rectangle:
size: self.size
pos: self.pos
Label:
background_color:(150/255, 150/255, 150/255, 1)
text:"table"
canvas.before:
Color:
rgba: self.background_color
Rectangle:
size: self.size
pos: self.pos
BoxLayout:
size_hint: 1, 1
Label:
background_color:(94/255, 94/255, 94/255, 1)
text:"tab"
canvas.before:
Color:
rgba: self.background_color
Rectangle:
size: self.size
pos: self.pos
Yes, you can avoid writting the same code for every Label. As all your labels have the same style, you may create a custom Label class in your .py file:
from kivy.uix.label import Label # Don't forget to import Label class
class CustomLabel(Label):
pass
Then, in your .kv file customize that class:
<CustomLabel>:
background_color:(150/255, 150/255, 150/255, 1)
text:"table"
canvas.before:
Color:
rgba: self.background_color
Rectangle:
size: self.size
pos: self.pos
Now, you're able to call CustomLabel in your .kv file, instead of the whole code for every label. The example below produces the same result you already have.
<Win>
box1:box1
BoxLayout:
size: root.size
orientation: "vertical"
BoxLayout:
size_hint: 1, 5
ScrollView:
GridLayout:
id:box1
orientation: 'tb-lr'
height: self.minimum_height
size_hint_y: None
row_default_height:180
spacing: 2
cols:1
CustomLabel:
CustomLabel:
CustomLabel:
CustomLabel:
As you can see you only have to call CustomLabel:.
However, if you pretend to add a lot of Labels, the best way is to use a for loop within your python file.

Adaptive_width property of MDLabel is not working correctly

I am making an app using kivy & kivymd and in one part of it, I would like the labels to take as much space as the actual text.
This seems pretty straightforward with kivy itself but for some reason, nothing works with the MDLabel class. I tried setting the adaptive_width property to True and I also tried to directly set the width to the texture_size[0] property but none of them worked (and yes I installed kivymd directly from github).
Here is my code:
from kivy.lang import Builder
from kivymd.app import MDApp
class MainApp(MDApp):
def __init__(self, **kwargs):
super(MainApp, self).__init__(**kwargs)
self.kv = Builder.load_string('''
#:kivy 2.0.0
BoxLayout:
MDLabel:
text: "Supposedly adaptive width (KivyMD)"
font_size: "21sp"
halign: "center"
adaptive_width: True
# I also tried directly setting the width to the texture_size but the results were worse
# size_hint_x: None
# width: self.texture_size[0]
canvas.before:
Color:
rgba: .8, .1, .2, .5
Rectangle:
pos: self.pos
size: self.size
Widget:
MDSeparator:
orientation: "vertical"
Widget:
Label:
text: "Actual adaptive width (Standard Kivy)"
font_size: "21sp"
color: 0, 0, 0, 1
size_hint_x: None
width: self.texture_size[0]
canvas.before:
Color:
rgba: 0, .6, .2, .5
Rectangle:
pos: self.pos
size: self.size
''')
def build(self):
return self.kv
if __name__ == '__main__':
MainApp().run()
Here is my results:
I don't believe that MDLabel supports the adaptive_width property. In using the width: self.texture_size[0], it seems that you must also add the text_size: None, None to the MDLabel, and it seems that its location in the kv is important. Here is a version of part of your kv that seems to work:
BoxLayout:
MDLabel:
text: "Supposedly adaptive width (KivyMD)"
font_size: "21sp"
halign: "center"
# adaptive_width: True
# I also tried directly setting the width to the texture_size but the results were worse
size_hint_x: None
width: self.texture_size[0]
text_size: None, None # added, and must be in this location
canvas.before:
Color:
rgba: .8, .1, .2, .5
Rectangle:
pos: self.pos
size: self.size

How do I make a widget with the same size as an image in kivy

I want to have a banner on the bottom of my application which scales with varying width. However, I can't get the size of the widget to correspond to the size of the image and because of that, I leave some blank space underneath the banner itself. How can this be done?
Here my try:
FloatLayout:
canvas.before:
Color:
rgb: utils.get_color_from_hex("#6ec4c1")
Rectangle:
size: self.size
pos: self.pos
GridLayout:
cols: 3
pos_hint: {"top": 0.95}
size_hint: 1, 0.05
ImageButton:
source: "images/back_arrow.png"
on_release: app.change_screen("home_screen")
Image:
source: "images/icon.png"
Label:
text: ""
Image:
id: banner
keep_ratio: True
allow_stretch: True
size_hint: 1, None
source: "images/banner.png"
This code does place the picture at the bottom! But ideally, I'd like to have it stay at the bottom with full-screen width and scale in y to fit it. Is this possible?
What I want and it only works for this specific aspect ratio of the screen
When I change the width of the screen I'd like it to scale in y to fit the screen instead it keeps y constant
By default, the size_hint of any Widget is (1,1). which means that your Image will be the same size as its containing FloatLayout. So you can set values for size_hint, or you can set size_hint: None, None and then provide values for size. Also, pos_hint: {"bottom": 1} will not work, since bottom is not a supported key for pos_hint. If you want the Image at the bottom, use pos_hint: {'y':0}, but the default value for pos is (0,0), so you shouldn't even need the pos_hint: {'y':0}.
Also, if you set a size_hint to None without providing the corresponding width or height, then the default width or height (which is 100) will be used.
I think I am finally getting what you want. You can use BoxLayout instead of FloatLayout. The BoxLayout will set the size of its children to fit its available space (see documentation). So, perhaps something like this will work:
BoxLayout:
orientation: 'vertical'
canvas.before:
Color:
rgb: utils.get_color_from_hex("#6ec4c1")
Rectangle:
size: self.size
pos: self.pos
GridLayout:
cols: 3
pos_hint: {"top": 0.95}
size_hint: 1, 0.05
ImageButton:
source: "images/back_arrow.png"
on_release: app.change_screen("home_screen")
Image:
source: "images/icon.png"
Label:
text: ""
Image:
id: banner
keep_ratio: True
allow_stretch: True
# size_hint: 1, None
source: "images/banner.png"
Note that setting size_hint: 1, 0.05 in the GridLayout means that 95% of the BoxLayout will be given to the banner.

How to center buttons in Kivy?

I am new to Python UI programming, and I am trying out Kivy. I want to center some buttons on my screen, but the buttons do not move from the bottom right of the window. I was wondering if anyone could point out what I am doing wrong or am missing?
vgx.kv:
#:kivy 1.9.1
<VgxMainScreen>:
BoxLayout:
size_hint: 0.2,0.2
size: 200, 100
orientation: 'vertical'
Button:
text: 'Game Select'
Button:
text: 'Options'
Button:
text: 'Controllers'
<VgxUI>:
AnchorLayout:
anchor_x: 'center'
VgxMainScreen:
size_hint: 0.2,0.2
<VgxApp>:
FloatLayout:
VgxUI:
center: 0.5, 0.5
VgxApp.py:
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
kivy.require('1.9.1')
class VgxMainScreen(Widget):
pass
class VgxUI(Widget):
pass
class VgxApp(App):
def build(self):
return VgxUI()
if __name__ == '__main__':
VgxApp().run()
Edit: Best way to debug what's going on with you widgets is to change their background colors :).
canvas.before:
Color:
rgba: X, X, X, 1
Rectangle:
pos: self.pos
size: self.size
The problem was that your BoxLayout wasn't positioned relatively to its parent even though it was inside it.
What I did here is that I positioned the BoxLayout to its parent's size and position using:
size: self.parent.size
pos: self.parent.pos
I also moved the size property from VgxMainScreen to VgxUI because I assume this is more common use-case (you can have multiple VgxMainScreens each with different size). Full code with background colors:
#:kivy 1.9.1
<VgxMainScreen>:
size_hint: None, None
canvas.before:
Color:
rgba: 1, 0, 0, 1 # red
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
canvas.before:
Color:
rgba: 0, 0, 1, 1 # blue
Rectangle:
pos: self.pos
size: self.size
size: self.parent.size # important!
pos: self.parent.pos # important!
orientation: 'vertical'
Button:
text: 'Game Select'
Button:
text: 'Options'
Button:
text: 'Controllers'
<VgxUI>:
AnchorLayout:
canvas.before:
Color:
rgba: 1, 1, 1, 1 # white
Rectangle:
pos: self.pos
size: self.size
size: self.parent.size
anchor_x: 'center'
anchor_y: 'center'
VgxMainScreen:
size: 200, 100
<VgxApp>:
FloatLayout:
VgxUI:
center: 0.5, 0.5
There's yet another solution to this.
You can use RelativeLayout and all widgets inside it will position relatively to this layout.
<VgxMainScreen>:
size_hint: None, None
RelativeLayout:
pos: self.parent.pos
size: self.parent.size
BoxLayout:
orientation: 'vertical'
Button:
text: 'Game Select'
Button:
text: 'Options'
Button:
text: 'Controllers'
I've personally scratched my head many times when using Kivy and wondered why my widgets aren't positioned as I think they should :).
VgxMainScreen is a Widget, so it doesn't apply any positioning or sizing to its children (only Layouts do that), so the BoxLayout has the default position of (0, 0) and size of (100, 100).
Make VgxMainScreen inherit from something else, like a FloatLayout.

Categories

Resources