Python Kivy: Align text to the left side of a Label - python

I read the docs, and still don't know how to align the text inside a Kivy-Label to its left side. The text is centered from default. A halign = "left" didn't help.
Sorry, if the solution is obvious, but I simply don't find it.
EDIT:
Example code:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
class Example(App):
def build(self):
self.root = FloatLayout()
self.label = Label(text="I'm centered :(", pos=(0,0), size_hint=(1.0,1.0), halign="left")
self.label.text_size = self.label.size #no horizontal change
self.root.add_widget(self.label)
return self.root
Example().run()

According to the documentation, it appears that the new created label have a size which exactly fit the text length so you might not see any differences after setting the halign property.
It is recommended there to change the size property (as shown in the example)
text_size = self.size
which will set the size of the label to the widget containing it.
Then you should see that the label is correctly centered.
As Tshirtman pointed out, you also have to bind text_size property to size. Full working example:
#!/usr/bin/kivy
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
class Example(App):
def build(self):
self.root = FloatLayout()
self.label = Label(text="I'm aligned :)", size_hint=(1.0, 1.0), halign="left", valign="middle")
self.label.bind(size=self.label.setter('text_size'))
self.root.add_widget(self.label)
return self.root
Example().run()

I'm kind of late to the party, but another nice trick I found for this is if you create your labels using your own custom class, you can define in that class the on_size function to change the text_size to size.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
class MyLabel(Label):
def on_size(self, *args):
self.text_size = self.size
class Example(App):
def build(self):
self.root = FloatLayout()
self.label = MyLabel(text="I'm aligned :)", size_hint=(1.0, 1.0), halign="left", valign="middle")
self.root.add_widget(self.label)
return self.root
Example().run()
Example with on_size:
Example without on_size:

Related

How can I center my GridLayout in the middle of the screen in Kivy?

I have a GridLayout with 8 cols, and I add 64 Buttons. (so 8x8).
I want the Buttons to ALWAYS be quadratic, so I made that in my spot_init() function.
That all works great. When I make the Window smaller or bigger, the rest of my Screen gets black and the GridLayout stays in the Corner. But I wanted it to be centered.
For leftright that works perfectly fine but when i try applying that to updown as well, it does some weird things, I really cannot explain.
Some things I (maybe) found out:
When I do it exactly like right now, but in the code, the Y coord is 3 times as high as it should be for some reason.
When I then divide it by 3, it gets 7 times as high...
It doesn't change if I do it in .kv or in .py file
Moving GridLayout without RelativeLayout also doesn't work (almost the same thing happens)
Other askers seemed to have the same problem (or a similiar one) but their fixes didn't help me.
My .kv file:
RMainBoard:
<MainBoard>:
cols:8
# height: self.minimum_height
# size_hint_y: None
# size_hint_x: None
<RMainBoard#RelativeLayout>:
pos:(self.width/2-min(self.width/8,self.height/8)*4,self.height/2-(min(self.width/8,self.height/8))*4)
MainBoard:
My .py file:
#resize window (NEEDS TO BE AT VERY TOP)
from kivy.config import Config
Config.set('graphics', 'width', '600')
Config.set('graphics', 'height', '600')
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.metrics import dp
from kivy.properties import NumericProperty
class MainBoard(GridLayout):
spots = []
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.spot_init()
def on_size(self,*args):
for spot in self.spots:
spot_size = min(self.width/8,self.height/8)
print(min(self.width/8,self.height/8))
spot.height = spot_size
spot.width = spot_size
def spot_init(self):
for i in range(0,64):
self.spots.append(Button(size_hint=(None,None),height=self.height/8,width=self.width/8))
self.add_widget(self.spots[i])
class TestApp(App):
pass
TestApp().run()
Thanks a lot <3
Rather than writing your own code to position the GridLayout, you can use the kv language to accomplish that. Here is a modified version of your code that does this:
# resize window (NEEDS TO BE AT VERY TOP)
from kivy.config import Config
from kivy.lang import Builder
Config.set('graphics', 'width', '600')
Config.set('graphics', 'height', '600')
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
kv = '''
RMainBoard:
<RMainBoard#RelativeLayout>:
MainBoard:
cols: 8
size_hint: None, None
# make MainBoard square
width: min(root.width, root.height)
height: self.width
# position MainBoard in center of RMainBoard
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
<SquareButton>:
size_hint: None, None
# make the Button square
width: min(self.parent.width/8, self.parent.height/8) if self.parent else 100
height: self.width
'''
class SquareButton(Button):
pass
class MainBoard(GridLayout):
spots = []
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.spot_init()
def spot_init(self):
for i in range(0, 64):
self.spots.append(SquareButton(text=str(i)))
self.add_widget(self.spots[i])
class TestApp(App):
def build(self):
return Builder.load_string(kv)
TestApp().run()
Note that I included the kv as a string in the python code. That was just for my own convenience, and it could just as well be in your kv file.
I have defined a SquareButton class along with a <SquareButton> rule in the kv that keeps the SquareButton square. The Buttons created in the spot_init() method are now SquareButtons.

Kivy: How to make label size and size of a canvas equal?

I have made a square shaped grid of labels using Gridlayout. Now i want to add a background color the labels(each having different rectangles). I tried to do this by the following code.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.graphics import Rectangle, Color
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
class MyGrid(FloatLayout):
def __init__(self,**kwargs):
super(MyGrid,self).__init__(**kwargs)
self.grid=GridLayout()
self.grid_size=4
self.grid.cols=self.grid_size
for k in range(self.grid_size):
for i in range(self.grid_size):
with self.grid.canvas:
Rectangle(size=(100,100),pos=(k*160+100,i*160+100),source="52852.JPG")
for h in range(self.grid_size):
for j in range(self.grid_size):
self.grid.add_widget(Label(text="labwl"+str(h)+str(j),size=(100,100),pos=(h*160+100,j*160+100)))
self.add_widget(self.grid)
class GameApp(App):
def build(self):
return MyGrid()
if __name__ == '__main__':
GameApp().run()
In this code if I do not specify "self.grid.cols" it generates a warning in the python console and also when the window is turned to full screen mode the rectangles from canvas retain there original size and position but the labels do not. I want to get the labels in front of the rectangles of canvas and they should also retain the size of the screen as specified. Moreover if I change the "self.grid.size" to any other number it should make the grid of labels of that length and corresponding number of canvas too. I tried float layout for this purpose also but it was of no help. The canvas rectangles and labels should fit in the window whatever the size of window has. It would be better if I can get the solution to above problem written in python file(not in .kv file). If you know any other solution to this problem or any other widget please let me know. Like for button widget we can specify the background color and text also your can add any of that widget which will do above task. You should replace the "source" in the rectangle canvas to any known image file. I hope you understand. If you do not please do let me know. :)
Setting the Widgets to not change size or pos is the easiest solution. Basically just use size_hint=(None, None) and don't use the GridLayout:
from kivy.app import App
from kivy.graphics import Rectangle
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
class MyGrid(FloatLayout):
def __init__(self,**kwargs):
super(MyGrid,self).__init__(**kwargs)
self.grid_size=4
for k in range(self.grid_size):
for i in range(self.grid_size):
with self.canvas:
Rectangle(size=(100,100),pos=(k*160+100,i*160+100),source="52852.JPG")
for h in range(self.grid_size):
for j in range(self.grid_size):
self.add_widget(Label(text="labwl"+str(h)+str(j),size=(100,100),pos=(h*160+100,j*160+100), size_hint=(None, None)))
class GameApp(App):
def build(self):
return MyGrid()
if __name__ == '__main__':
GameApp().run()
To make the Rectangles and Labels change pos and size is a bit more complex. In the modified version of your code below, I keep lists of the Labels and the Rectangles, and bind the adjust_common_size() method to run whenever the size of MyGrid changes. That method then adjusts the size and pos of the Labels and Rectangles to match:
from kivy.app import App
from kivy.properties import ListProperty
from kivy.graphics import Rectangle
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
class MyGrid(FloatLayout):
common_size = ListProperty((100, 100))
def __init__(self,**kwargs):
super(MyGrid,self).__init__(**kwargs)
self.grid_size=4
self.rects = []
self.labels = []
for k in range(self.grid_size):
one_row = []
self.rects.append(one_row)
for i in range(self.grid_size):
with self.canvas:
one_row.append(Rectangle(size=self.common_size,pos=(k*160+100,i*160+100),source="52852.JPG"))
for h in range(self.grid_size):
one_row = []
self.labels.append(one_row)
for j in range(self.grid_size):
label = Label(text="labwl"+str(h)+str(j),size=self.common_size,pos=(h*160+100,j*160+100), size_hint=(None, None))
one_row.append(label)
self.add_widget(label)
self.bind(size=self.adjust_common_size)
def adjust_common_size(self, instance, new_size):
self.common_size = (new_size[0] * 0.9 / self.grid_size, new_size[1] * 0.9 / self.grid_size)
for k in range(self.grid_size):
for i in range(self.grid_size):
adjusted_pos = (k * new_size[0] / self.grid_size, i * new_size[1] / self.grid_size)
rect = self.rects[k][i]
label = self.labels[k][i]
label.size = self.common_size
label.pos = adjusted_pos
rect.size = self.common_size
rect.pos = adjusted_pos
class GameApp(App):
def build(self):
return MyGrid()
if __name__ == '__main__':
GameApp().run()
Using a ListProperty for the common_size is not necessary, but would be handy if you decide to use kv.
This is an interesting question. Here is a better way to make the Rectangle and the Label match. The code below uses the GridLayout, but defines MyLabel to include its own Rectangle and to keep its Rectangle matched in pos and size:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.graphics import Rectangle
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
class MyLabel(Label):
def __init__(self, **kwargs):
super(MyLabel, self).__init__(**kwargs)
with self.canvas.before:
self.rect = Rectangle(size=self.size,pos=self.pos,source="52852.JPG")
self.bind(size=self.adjust_size)
self.bind(pos=self.adjust_pos)
def adjust_pos(self, instance, new_pos):
self.rect.pos = new_pos
def adjust_size(self, instance, new_size):
self.rect.size = new_size
class MyGrid(FloatLayout):
def __init__(self,**kwargs):
super(MyGrid,self).__init__(**kwargs)
self.grid=GridLayout()
self.grid_size=4
self.grid.cols=self.grid_size
for h in range(self.grid_size):
for j in range(self.grid_size):
self.grid.add_widget(MyLabel(text="labwl"+str(h)+str(j),size=(100,100),pos=(h*160+100,j*160+100)))
self.add_widget(self.grid)
class GameApp(App):
def build(self):
return MyGrid()
if __name__ == '__main__':
GameApp().run()
With this approach, you don't have to create the Rectangles in the MyGrid at all, since each Label creates its own.

python- Kivy Screen Manager within .py file

I am building a multiple screen App with Kivy and I would like to use the ScreenManager to navigate between the multiple screens. I have seen examples and documentation for how to create the screens within a .kv file, but I want to know how to create them within the .py file.
Problem: When I create the screen subclasses as shown below, my app
window returns a blank screen.
Question: What is the correct way to
create the Screen subclasses within a .py file?
Right now I have two Screen subclasses defined: 'welcomeScreen', and 'functionScreen'. Each consists of a layout with some widgets.
kivy.require('1.9.1')
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
import kivy.uix.boxlayout
import kivy.uix.button
from kivy.uix.screenmanager import ScreenManager, Screen
class PanelBuilderApp(App): # display the welcome screen
def build(self):
# Create the screen manager and add widgets to the base sm widget
sm = kivy.uix.screenmanager.ScreenManager()
sm.add_widget(Screen(name='welcomeScreen'))
sm.add_widget(Screen(name='functionScreen'))
# sm.current= 'welcomeScreen'
return sm
class welcomeScreen(Screen): #welcomeScreen subclass
def __init__(self, **kwargs): #constructor method
super(welcomeScreen, self).__init__(**kwargs) #init parent
welcomePage = FloatLayout()
box = kivy.uix.boxlayout.BoxLayout(orientation='vertical', size_hint=(0.4, 0.3),
padding=8, pos_hint={'top': 0.5, 'center_x': 0.5})
welcomeLabel = Label(text='Hello and welcome to the Panel Builder version 1.0.\nApp by John Vorsten\nClick below to continue',
halign= 'center', valign= 'center', size_hint= (0.4, 0.2), pos_hint= {'top': 1, 'center_x': 0.5})
welcomeBox = kivy.uix.button.Button(text= 'Click to continue')
welcomeBox.bind(on_press= self.callback)
welcomeBox2 = kivy.uix.button.Button(text='not used')
welcomePage.add_widget(welcomeLabel)
box.add_widget(welcomeBox)
box.add_widget(welcomeBox2)
welcomePage.add_widget(box)
self.add_widget(welcomePage)
def callback(instance):
print('The button has been pressed')
sm.switch_to(Screen(name= 'functionScreen'))
# sm.current = Screen(name= 'functionScreen')
class functionScreen(Screen): #For later function navigation
def __init__(self, **kwargs): #constructor method
super(functionScreen, self).__init__(**kwargs) #init parent
functionPage = kivy.uix.floatlayout.FloatLayout()
functionLabel = Label(text='Welcome to the function page. Here you will choose what functions to use',
halign='center', valign='center', size_hint=(0.4,0.2), pox_hint={'top': 1, 'center_x': 0.5})
functionPage.add_widget(functionLabel)
self.add_widget(functionPage)
# sm.add_widget('Name') #Add more names later when you create more screens
# OR#
# for i in ScreenDirectory:
# sm.add_widget(ScreenDirectory[i])
PanelBuilderApp().run()
if __name__ == '__main__':
pass
I understand I can add the definitions to a .kv file, and I will probably do this as the app grows. However, I like being explicit as I am learning how to use kivy.
I think you think using Screen(name='welcomeScreen') you are using welcomeScreen but that is not true, if you want to use welcomeScreen you should use it directly.
On the other hand you have typographical errors so I have corrected, I recommend you follow the kivy tutorials, obviously you must have a solid OOP base (and I think you do not have it so your task is to reinforce).
import kivy
kivy.require('1.9.1')
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
class PanelBuilderApp(App): # display the welcome screen
def build(self):
sm = ScreenManager()
sm.add_widget(WelcomeScreen(name='welcomeScreen'))
sm.add_widget(FunctionScreen(name='functionScreen'))
return sm
class WelcomeScreen(Screen): #welcomeScreen subclass
def __init__(self, **kwargs): #constructor method
super(WelcomeScreen, self).__init__(**kwargs) #init parent
welcomePage = FloatLayout()
box = BoxLayout(orientation='vertical', size_hint=(0.4, 0.3),
padding=8, pos_hint={'top': 0.5, 'center_x': 0.5})
welcomeLabel = Label(text='Hello and welcome to the Panel Builder version 1.0.\nApp by John Vorsten\nClick below to continue',
halign= 'center', valign= 'center', size_hint= (0.4, 0.2), pos_hint= {'top': 1, 'center_x': 0.5})
welcomeBox = Button(text= 'Click to continue', on_press=self.callback)
welcomeBox2 = Button(text='not used')
welcomePage.add_widget(welcomeLabel)
box.add_widget(welcomeBox)
box.add_widget(welcomeBox2)
welcomePage.add_widget(box)
self.add_widget(welcomePage)
def callback(self, instance):
print('The button has been pressed')
self.manager.current = 'functionScreen'
class FunctionScreen(Screen): #For later function navigation
def __init__(self, **kwargs): #constructor method
super(FunctionScreen, self).__init__(**kwargs) #init parent
functionPage = FloatLayout()
functionLabel = Label(text='Welcome to the function page. Here you will choose what functions to use',
halign='center', valign='center', size_hint=(0.4,0.2), pos_hint={'top': 1, 'center_x': 0.5})
functionPage.add_widget(functionLabel)
self.add_widget(functionPage)
if __name__ == '__main__':
PanelBuilderApp().run()

Is ScreenManager compatible with DropDown in Kivy?

I want to generate a dropdown-list in my second screen managed by Kivys ScreenManager. If I do so, I get this traceback:
...
File "C:/Users/ORANG/PycharmProjects/waldi/playground/cw.py", line 76, in on_text
instance.drop_down.open(instance)
File "C:\Kivy-1.9.0-py2.7-win32-x64\kivy27\kivy\uix\dropdown.py", line 215, in open
'Cannot open a dropdown list on a hidden widget')
kivy.uix.dropdown.DropDownException: Cannot open a dropdown list on a hidden widget
This is the code, which is basically the same as in this
example, just embedded in a screenmanager scenario simple as can be:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.textinput import TextInput
from kivy.properties import ListProperty, StringProperty
import re
from kivy.lang import Builder
Builder.load_string('''
<Lieferant>:
ComboLayout:
Label:
text: 'Label'
ComboEdit:
size_hint: .5, .3
pos_hint: {'center':(.5, .5)}
# `args` is the keyword for arguments passed to `on_text` in kv language
on_text: self.parent.on_text(self, args[1])
''')
class ComboEdit(TextInput):
"""
This class defines a Editable Combo-Box in the traditional sense
that shows it's options
"""
options = ListProperty(('',))
'''
:data:`options` defines the list of options that will be displayed when
touch is released from this widget.
'''
def __init__(self, **kw):
ddn = self.drop_down = DropDown()
ddn.bind(on_select=self.on_select)
super(ComboEdit, self).__init__(**kw)
def on_options(self, instance, value):
ddn = self.drop_down
# clear old options
ddn.clear_widgets()
for option in value:
# create a button for each option
but = Button(text=option,
size_hint_y=None,
height='36sp',
# and make sure the press of the button calls select
# will results in calling `self.on_select`
on_release=lambda btn: ddn.select(btn.text))
ddn.add_widget(but)
def on_select(self, instance, value):
# on selection of Drop down Item... do what you want here
# update text of selection to the edit box
self.text = value
class ComboLayout(BoxLayout):
rtsstr = StringProperty("".join(("Substrate1,,,Substrate1,,,Substrate1,,,",
"Substrate1,,,Substrate1,,,Substrate_coating",
",,,silicon,,,silicon_Substrate,,,substrate_",
"silicon,,,")))
def on_text(self, instance, value):
if value == '':
instance.options = []
else:
match = re.findall("(?<=,{3})(?:(?!,{3}).)*?%s.*?(?=,{3})" % value, \
self.rtsstr, re.IGNORECASE)
# using a set to remove duplicates, if any.
instance.options = list(set(match))
instance.drop_down.open(instance)
class Intro(Screen):
pass
class Lieferant(Screen):
pass
class CWApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(Intro(name='Intro'))
sm.add_widget(Lieferant(name='Lieferant'))
return sm
if __name__ == '__main__':
CWApp().run()
Is it possible to combine them? How would you do this?
This code is running if I just comment this line out, which adds a screen before the screen with the dropdown:
sm.add_widget(Intro(name='Intro'))
Ok - for this question I got the "Tumbleweed Badget" - LOL
I admit it's a very special one and makes obvious, how much I am a beginner here. So I asked another question based on the same problem and spend some effort to make it easier to understand the code and to reproduce it. This paid off! Look here for the solution if you tumble once into that kind of problem: Is it a bug in Kivy? DropDown + ScreenManager not working as expected

How to allow user to choose file as background image in kivy?

Or, how to do so using python, give it an ID, and set it as background image in kv language?
I would like to be able to draw on top of an image instead of a black screen, which I am doing here:
edited
new problem: upload button does not work, here is new code
from random import random
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.graphics import Color, Line, Rectangle
from kivy.uix.filechooser import FileChooserListView, FileChooserIconView
from kivy.uix.floatlayout import FloatLayout
class MyPaintWidget(Widget):
def on_touch_down(self, touch):
color = (random(), random(), random())
with self.canvas:
Color(*color)
d = 30.
touch.ud['line'] = Line(points=(touch.x, touch.y))
def on_touch_move(self, touch):
touch.ud['line'].points += [touch.x, touch.y]
class MyPaintApp(App):
def build(self):
parent = Widget()
painter = MyPaintWidget()
Choose = Button(text = 'upload image')
parent.add_widget(painter)
parent.add_widget(Choose)
def chooose_file(obj):
fc = FileChooserIconView(title= 'upload image')
image_path = self.fc.selection[0]
image_name = file_path.split('/')[-1]
with self.canvas.before:
Rectangle(
size=self.size,
pos=self.pos,
source=image_name)
Choose.bind(on_release=choose_file)
return parent
if __name__ == '__main__':
MyPaintApp().run()
What about this:
If you used the kivy filechooser to get the user to select an image file,
then you could use the .selection attribute of the filechooser to get the name and/or path of that file. Once you have that, you could use it to set the source of a Rectangle on the canvas of the layout etc. where you want the background image.
For instance, to set a background image on a BoxLayout, inside the class that inherits from the BoxLayout:
fc = FileChooserIconView(title="Choose Image")
image_path = self.fc.selection[0]
image_name = file_path.split('/')[-1]
with self.canvas.before:
Rectangle(
size=self.size,
pos=self.pos,
source=image_name)
This is of course a very simplistic example, and isn't really taking the rest of your code into account, but with the kivy docs on FileChooser you should get it. Worth noting also that you could do this in the kv file, perhaps much more cleanly.

Categories

Resources