How to center buttons in Kivy? - python

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.

Related

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

Kivy align Switch left in BoxLayout

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

How to change label background color dynamically in Kivy

I try to make a simple ToDoList program.There are add, remove and do it buttons. But I have some bugs about labels color. When I click "DO IT" button label color change in scrollview but when I click remove button when some of them done, colored labels change. I did using canvas. How can I fix this problem?
class Home(Screen):
def __init__(self,**kwargs):
super(Home,self).__init__(**kwargs)
def addWidget(self):
task_input = self.ids.task_input.text
newListItem = EachTask(text=task_input ,
id=str((len(self.ids.add_field.children))) )
print(newListItem.id)
self.ids.add_field.add_widget(newListItem)
class EachTask(BoxLayout):
def __init__(self, text= "", **kwargs):
super(EachTask,self).__init__(**kwargs)
self.ids.label.text = text
def Do_Task(self,instance):
child = instance.parent.parent
with self.canvas.before:
Color(.5,1,.2,1, mode='rgba')
Rectangle(pos=child.ids.label.pos, size=child.ids.label.size)
kv_file
<FlatButton#ButtonBehavior+Label>:
font_size: 15
<Home>:
BoxLayout:
id: home
orientation: "vertical"
spacing: 5
#space_x: self.size[0]/2
canvas.before:
Color:
rgba: (1,1,1,1)
Rectangle:
size: self.size
pos: self.pos
##########HEADER#######
BoxLayout:
id: header
size_hint_y: None
height: 50
canvas.before:
Color:
rgba: (.85,.7,.2,1)
Rectangle:
size: self.size
pos: self.pos
Label:
text: "TO DO LIST"
font_size: "20sp"
bold: True
size_hint_x: .9
FlatButton:
text: "Back"
size_hint_x: .1
####################################
ScrollView:
canvas.before:
Color:
rgba: (1,1,.2,.2)
Rectangle:
size: self.size
pos: self.pos
BoxLayout:
id: add_field
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
spacing: 2 #Spaces between childs
#####################################################
BoxLayout:
id: input_field
size_hint_y: None
height: 80
TextInput:
id: task_input
focus: True
size_hint_x: .9
multiline: False
Button:
font_size: "40sp"
size_hint_x: .1
text: "+"
on_release: root.addWidget()
id: button1
color: 1,0.5,0.5,1
#######################################################
<EachTask>:
size_hint_y: None
height: 50
id: each_task
BoxLayout:
Label:
size_hint_x: .8
id: label
canvas.before:
Color:
rgba: (1,.2,.2,.2)
Rectangle:
size: self.size
pos: self.pos
Button:
size_hint_x: .1
text: "X"
on_release: app.root.ids.add_field.remove_widget(root)
Button:
size_hint_x: .1
text: "DO IT"
on_release: root.Do_Task(self)
The following enhancements are required to the kv and py files to solve the problem.
Method 1 - Kivy automatically created & added an ObjectProperty, rgba
Kivy automatically created & added an ObjectProperty
If the widget doesn’t have a property with the given name, an
ObjectProperty will be automatically created and added to the widget.
kv file
Add a class attribute, rgba and initialize it to default colour, (1, .2, .2, .2) to class rule, <EachTask>:
Replace label's colour to root.rgba
Snippets - kv file
<EachTask>:
rgba: (1,.2,.2,.2) # Kivy auto created & added ObjectProperty, "rgba"
...
BoxLayout:
Label:
size_hint_x: .8
id: label
canvas.before:
Color:
rgba: root.rgba
...
py file
Remove all the codes in method Do_Task()
Add self.rgba = [.5, 1, .2, 1] whereby self refers to the current widget i.e. EachTask object.
Snippets - py file
def Do_Task(self, instance):
self.rgba = [.5, 1, .2, 1]
Method 2 - Explicitly declaring rgba
kv file
Replace rgba: (1,.2,.2,.2) with root.rgba
Snippets - kv file
<EachTask>:
...
BoxLayout:
Label:
size_hint_x: .8
id: label
canvas.before:
Color:
rgba: root.rgba
...
py file
Add import statement, from kivy.properties import ListProperty
Declare class attribute, rgba of ListProperty type and initilaize it to default colour, [1, .2, .2, .2] in class EachTask()
Remove all the codes in method Do_Task()
Add self.rgba = [.5, 1, .2, 1] whereby self refers to the current widget i.e. EachTask object.
Snippets - py file
from kivy.properties import ListProperty
...
class EachTask(BoxLayout):
rgba = ListProperty([1, .2, .2, .2])
...
def Do_Task(self, instance):
self.rgba = [.5, 1, .2, 1]

how to center canvas? kivy

How to place the white square to the center? I tried a lot of variants, but nothing worked. It work for Label and Buttons, but not for canvas. Or maybe I'm doing everything in wrong way. maybe you suggest the best solution for this task. I need window with background, label in left corner, label in right corner and a square in center
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.config import Config
from kivy.animation import Animation
from kivy.vector import Vector
from kivy.clock import Clock
from kivy.properties import NumericProperty, ReferenceListProperty,\
ObjectProperty
Config.set('graphics', 'resizable', 'true')
Config.set('graphics', 'width', '900')
Config.set('graphics', 'height', '450')
Config.write()
class Helicopter(Widget):
pass
class Background(Widget):
pass
class Root(FloatLayout):
#def on_touch_down(self, touch):
# Animation(center=touch.pos).start(self)
pass
class FriendsApp(App):
def build(self):
return Root()
if __name__ == '__main__':
FriendsApp().run()
.kv file
#: kivy 1.10.0
<Root>
AnchorLayout:
canvas.before:
Color:
rgba: 1, 1, 1, 1 # white
Rectangle:
source: 'background.jpg'
pos: self.pos
size: self.size
size: self.parent.size
anchor_x: 'center'
anchor_y: 'center'
AnchorLayout:
anchor_y:'top'
anchor_x:'left'
padding: 20
Label:
text: 'Lives: x2'
size: self.texture_size
size_hint: None, None
AnchorLayout:
anchor_x: 'right'
anchor_y: 'top'
padding: 20
Label:
text: 'Score: 0000000'
size: self.texture_size
size_hint: None,None
AnchorLayout:
anchor_x: 'center'
anchor_y: 'center'
canvas:
Rectangle:
size: 100, 100
pos: self.pos
The AnchorLayout has its own canvas and you cannot aligns itself. There are two solutions to this problem. In the example, colors were added for visualization.
The AnchorLayout aligns its children to a border (top, bottom,
left, right) or center.
Solution 1
Add a Widget as a children.
AnchorLayout:
anchor_x: 'center'
anchor_y: 'center'
Widget:
canvas.before:
Color:
rgba: 1, 1, 1, 1 # white
Rectangle:
size: 100, 100
pos: self.pos
size_hint: None,None
Solution 2
Replace the last AnchorLaoyout with Widget.
Widget:
canvas:
Rectangle:
pos: self.center_x - 50, self.center_y - 50
size: 100, 100
Example - Solution 1
kv file
#: kivy 1.10.0
<Root>
AnchorLayout:
anchor_y:'top'
anchor_x:'left'
padding: 20
Label:
canvas.before:
Color:
rgba: 1, 0, 0, 1 # red
Rectangle:
pos: self.pos
size: self.size
text: 'Lives: x2'
size: self.texture_size
size_hint: None, None
AnchorLayout:
anchor_x: 'right'
anchor_y: 'top'
padding: 20
Label:
canvas.before:
Color:
rgba: 0, 0, 1, 1 # blue
Rectangle:
pos: self.pos
size: self.size
text: 'Score: 0000000'
size: self.texture_size
size_hint: None,None
AnchorLayout:
anchor_x: 'center'
anchor_y: 'center'
Widget:
canvas.before:
Color:
rgba: 1, 1, 1, 1 # white
Rectangle:
size: 100, 100
pos: self.pos
size_hint: None,None
Output

Categories

Resources