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.
Related
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.
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
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
So my problem is that I want to have a button which uses an image from an URL. However the image doesn't show in the correct place but the button does. The image is not clickable but the button is. I want the image to be on the button and to be clickable and to send me to the following screen (ProfileScreen)
Kv file:
<Main_app>:
name: "Main_app"
canvas:
Color:
rgb: 0.216, 0.569, 0.639
Rectangle:
pos: self.pos
size: self.size
Label:
text: ""
color: 0,0,0,1
canvas.before:
Color:
rgba: 0.251, 0.655, 0.737,1
Rectangle:
pos: self.pos
size: self.size
pos_hint: {"left":1, "top":1}
size_hint: 1, 0.0995
Label:
text: ""
color: 0,0,0,1
canvas.before:
Color:
rgba: 0.173, 0.451, 0.51,1
Rectangle:
pos: self.pos
size: self.size
pos_hint: {"left":1, "top":0.9}
size_hint: 1, 0.01
Label:
text: ""
color: 0,0,0,1
canvas.before:
Color:
rgba: 0.251, 0.655, 0.737,1
Rectangle:
pos: self.pos
size: self.size
pos_hint: {"left":1, "top":0.12}
size_hint: 1, 0.25
Label:
text: ""
color: 0,0,0,1
canvas.before:
Color:
rgba: 0.173, 0.451, 0.51,1
Rectangle:
pos: self.pos
size: self.size
pos_hint: {"left":1, "top":0.13}
size_hint: 1, 0.01
Button:
size_hint: 0.1, 0.1
size: 100,100
pos_hint: {"x":0.90, "top":1.0}
on_press: app.root.current = "OpeningScreen"
AsyncImage:
source: 'https://cdn2.iconfinder.com/data/icons/flat-ui-icons-24-px/24/eye-24-256.png'
keep_ratio: True
pos_hint: {"x":0.90, "top":1.0}
<ProfileScreen>:
name: "ProfileScreen"
canvas:
Color:
rgb: 0.216, 0.569, 0.639
Rectangle:
pos: self.pos
size: self.size
Python file:
class ProfileScreen(Screen):
pass
class Main_app(Screen):
pass
AppKv = Builder.load_file("App.kv")
class MyApp(App):
def build(self):
return AppKv
if __name__ == '__main__':
MyApp().run()
Here are some pictures of how it looks like.
http://prntscr.com/iihmcc
You could create a new widget type which implements Image and ButtonBehavior. I had the same problem and found that somewhere. Here is the code I used to create that new widget. It is not for a .kv file though.
First you create a new class with extending the Image and ButtonBehavior class:
class ImageButton(ButtonBehavior, Image):
def on_press(self):
print("on_press")
Then you can create a new instance of the class you just created with a function like this:
#staticmethod
def create_image_button_widget(file_name):
"""
Function used to create an ImageButton widget.
:param file_name: Filename of the image to be used as source
:return: The ImageButton object.
"""
image_to_add = ImageButton()
image_to_add.source = "source_of_image.jpg"
image_to_add.name = file_name
return image_to_add
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.