Kivy: Create a list of coloured labels using build() method - python

I want to create a list of coloured labels. The thing is that I could do it with the kv file, but I need to do it through the build() method. So I tried replicate what I have done, but it does not work. And I can't understand why.
This is what I've coded
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics import *
class RL(RelativeLayout): # Creates the background colour for each label
def __init__(self, **kwargs):
super().__init__(**kwargs)
with self.canvas:
Color(.7, 0, .5, 1)
Rectangle(size_hint=self.size)
class MainMenu(BoxLayout):
N_LBLS = 8
labels_text = []
RL_list = []
def __init__(self, **kwargs):
super().__init__(**kwargs)
button = Button(text='do something')
button.bind(on_release=self.change_text)
box = BoxLayout(orientation='vertical', padding= 10, spacing = 15)
for i in range(0, self.N_LBLS):
self.RL_list.append(RL())
self.labels_text.append(Label(text=f'{i}º label', size_hint=self.size))
self.RL_list[i].add_widget(self.labels_text[i])
box.add_widget(self.RL_list[i])
self.add_widget(button)
self.add_widget(box)
def change_text(self, instance):
for lbl in self.labels_text:
if lbl.text[0] == '5':
lbl.text = 'Text changed'
class MainApp(App):
def build(self):
return MainMenu()
if __name__ == '__main__':
MainApp().run()
It's supposed to make a button to the left, and a list of 8 coloured labels to the right.

The problem is that you are setting size_hint=self.size in each Label. The self.size is the size of the MainMenu, which is [100,100] when that code is executed. Note that size_hint is a multiplier that is applied to the parents size to calculate the widgets size. So a size_hint of [100,100] makes each Label 100 times bigger than the MainMenu. So your code is working, but the Labels are so large that the text is off the screen. Start by just removing size_hint=self.size.
And, to set a background color on a Label, you can just use the canvas of that Label, rather than some container. Here is a version of your code that does that:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
class ColorLabel(Label):
pass
Builder.load_string('''
<ColorLabel>:
bg_color: [.7, 0, .5, 1]
canvas.before:
Color:
rgba: self.bg_color
Rectangle:
pos: self.pos
size: self.size
''')
class MainMenu(BoxLayout):
N_LBLS = 8
labels_text = []
def __init__(self, **kwargs):
super().__init__(**kwargs)
button = Button(text='do something')
button.bind(on_release=self.change_text)
box = BoxLayout(orientation='vertical', padding=10, spacing=15)
for i in range(0, self.N_LBLS):
self.labels_text.append(ColorLabel(text=f'{i}º label'))
box.add_widget(self.labels_text[i])
self.add_widget(button)
self.add_widget(box)
def change_text(self, instance):
for lbl in self.labels_text:
if lbl.text[0] == '5':
lbl.text = 'Text changed'
lbl.bg_color = [0, 1, 0, 1]
class MainApp(App):
def build(self):
return MainMenu()
if __name__ == '__main__':
MainApp().run()

Related

kivy transition between screens

i made a gui with python kivy with a starting screen that has a button in it which generates new screens when pressed, and it also generates new buttons so its easier to pick the screen i need to get in focus but i can't get it working properly cause i can't generate buttons on the newly made screens
when they aren't in focus, the script only generates buttons for the screen in focus it seems
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
class ScreenManagement(ScreenManager):
def __init__(self, **kwargs):
super(ScreenManagement, self).__init__(**kwargs)
sm = ScreenManagement(transition=FadeTransition())
class newtab(Screen):
def __init__(self, **kwargs):
super(newtab, self).__init__(**kwargs)
self.bt1=(Button(text="New tab", size_hint =(.1, .1) ,pos_hint ={'center_x':.1, 'center_y':.94}))
self.add_widget(self.bt1)
self.bt1.bind(on_release=self.new_tab)
self.txt1 = TextInput(text='',size_hint =(.1, .1), pos_hint ={'center_x':.2, 'center_y':.75}, multiline=True)
self.add_widget(self.txt1)
def transition(self, instance):
self.manager.current = (instance.text)
for item in sm.screen_names:
self.bt2=(Button(text=item, size_hint =(.1, .1) ,pos_hint ={'center_x':(.1*sm.screen_names.index(item)+.1), 'center_y':.84}))
self.add_widget(self.bt2)
self.bt2.bind(on_release=self.transition)
def new_tab(self, *args):
n = len(self.manager.screen_names) ##number of screens+1
screen = newtab(name="screen {}".format(n)) #make new screen and give it number
self.manager.add_widget(screen)
self.manager.current = "screen 0"
for item in sm.screen_names:
self.bt2=(Button(text=item, size_hint =(.1, .1) ,pos_hint ={'center_x':(.1*sm.screen_names.index(item)+.1), 'center_y':.84}))
self.add_widget(self.bt2)
self.bt2.bind(on_release=self.transition)
sm.add_widget(newtab(name='screen 0'))
class Application(App):
def build(self):
return sm
if __name__ == "__main__":
Application().run()
I suppose that below is what you expected to get. But I agree with #ApuCoder that you may look for TabbedPanel functionality.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
class ScreenManagement(ScreenManager):
def __init__(self, **kwargs):
super(ScreenManagement, self).__init__(**kwargs)
sm = ScreenManagement(transition=FadeTransition())
class newtab(Screen):
def __init__(self, **kwargs):
super(newtab, self).__init__(**kwargs)
self.bt1 = (Button(text="New tab", size_hint=(.1, .1), pos_hint={'center_x': .1, 'center_y': .94}))
self.add_widget(self.bt1)
self.bt1.bind(on_release=self.new_tab)
self.txt1 = TextInput(text='', size_hint=(.1, .1), pos_hint={'center_x': .2, 'center_y': .75}, multiline=True)
self.add_widget(self.txt1)
# add all present screen buttons to newly created screen
for i, screen in enumerate(sm.screens):
self.bt2 = (Button(text=screen.name, size_hint=(.1, .1), pos_hint={'center_x': (.1 * i + .1), 'center_y': .84}))
self.add_widget(self.bt2)
self.bt2.bind(on_release=self.transition)
# add this newly created screen button to all screens
for screen in sm.screens + [self]:
screen.bt2 = (Button(text=self.name, size_hint=(.1, .1), pos_hint={'center_x': (.1 * len(sm.screens) + .1), 'center_y': .84}))
screen.add_widget(screen.bt2)
screen.bt2.bind(on_release=screen.transition)
def transition(self, instance):
self.manager.current = instance.text
def new_tab(self, *args):
n = len(self.manager.screen_names) ##number of screens+1
screen = newtab(name="screen {}".format(n)) # make new screen and give it number
self.manager.add_widget(screen)
self.manager.current = "screen 0"
sm.add_widget(newtab(name='screen 0'))
class Application(App):
def build(self):
return sm
if __name__ == "__main__":
Application().run()

How do I control initial Size and positioning of labels and buttons in a splitter in Kivy?

It's me again, trying to understand Kivy concepts.
I have a widget with a base class of RelativeLayout containing a chessboard image displaying in a splitter. I want to display a label, and 2 buttons horizontally below the chessboard spaced a small distance away from the chessboard and still have everything resizable with splitter. I've tried numerous ways to no avail. What I currently have is this:
What I want is this: (How do I achieve it?)
Here is the code:
import kivy
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.splitter import Splitter
from kivy.uix.image import Image
kivy.require('2.0.0')
class ChessBoardWidget(RelativeLayout): # FloatLayout
def __init__(self, **kwargs):
super(ChessBoardWidget, self).__init__(**kwargs)
repertoire_boxlayout = BoxLayout(orientation='horizontal')
repertoire_boxlayout.add_widget(Label(text='Repertoire for:'))
repertoire_boxlayout.add_widget(Button(text='White'))
repertoire_boxlayout.add_widget(Button(text='Black'))
chessboard_gui_boxlayout = BoxLayout(orientation='vertical')
chessboard_gui_boxlayout.add_widget(
Image(source="./data/images/chess-pieces/DarkerGreenGreyChessBoard.png", pos=self.pos,
size_hint=(1, 1), keep_ratio=True, allow_stretch=True))
chessboard_gui_boxlayout.add_widget(repertoire_boxlayout)
self.add_widget(chessboard_gui_boxlayout)
class SplitterGui(BoxLayout):
def __init__(self, **kwargs):
super(SplitterGui, self).__init__(**kwargs)
self.orientation = 'horizontal'
# Splitter 1
split1_boxlayout = BoxLayout(orientation='vertical')
split1 = Splitter(sizable_from='bottom', min_size=74, max_size=1100)
chessboard_widget = ChessBoardWidget()
split1.add_widget(chessboard_widget)
split1_boxlayout.add_widget(split1)
s3_button = Button(text='s3', size_hint=(1, 1))
split1_boxlayout.add_widget(s3_button)
self.add_widget(split1_boxlayout)
# Splitter 2
split2 = Splitter(sizable_from='left', min_size=74, max_size=1800)
s2_button = Button(text='s2', size_hint=(.1, 1))
split2.add_widget(s2_button)
self.add_widget(split2)
class ChessBoxApp(App):
def build(self):
return SplitterGui() # root
if __name__ == '__main__':
ChessBoxApp().run()
In a BoxLayout (see the documentation), you can use size_hint and size (or height, width) to adjust sizes. So, you can set the height of your Buttons, and let the Image use the remaining height of the BoxLayout:
class ChessBoardWidget(RelativeLayout):
def __init__(self, **kwargs):
super(ChessBoardWidget, self).__init__(**kwargs)
repertoire_boxlayout = BoxLayout(orientation='horizontal', size_hint=(1, None), height=30) # set height of Buttons
repertoire_boxlayout.add_widget(Label(text='Repertoire for:'))
repertoire_boxlayout.add_widget(Button(text='White'))
repertoire_boxlayout.add_widget(Button(text='Black'))
chessboard_gui_boxlayout = BoxLayout(orientation='vertical')
chessboard_gui_boxlayout.add_widget(
Image(source="./data/images/chess-pieces/DarkerGreenGreyChessBoard.png", pos=self.pos, keep_ratio=True, allow_stretch=True)) # default size_hint of (1,1) claims all of remaining height
chessboard_gui_boxlayout.add_widget(repertoire_boxlayout)
self.add_widget(chessboard_gui_boxlayout)

Python Kivy - Adding a GridLayout to a BoxLayout

I want to create an UI with Buttons on top and a few Labels on bottom and if the labels exceeds the height it should be scrollable.
Something like this:
So far this is my code:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
class MyApp(App):
main_layout = BoxLayout(orientation='vertical')
top_layout = BoxLayout(orientation='horizontal')
scrollView = ScrollView()
gridLayout = GridLayout()
gridLayout.cols = 1
gridLayout.minimum_height = 10
gridLayout.padding = [0, 0, 0, 0]
scrollView.add_widget(gridLayout)
main_layout.add_widget(top_layout)
main_layout.add_widget(scrollView)
def btn_create(self, instance):
self.gridLayout.add_widget(Label(text='test'))
def btn_edit(self, instance):
pass
def btn_delete(self, instance):
pass
def build(self):
self.top_layout.size_hint=(1, .1)
# Button 'Erstellen'
btnCreate = Button()
btnCreate.text = 'Erstellen'
btnCreate.bind(on_press=self.btn_create)
# Button 'Bearbeiten'
btnEdit = Button()
btnEdit.text = 'Bearbeiten'
btnEdit.bind(on_press=self.btn_edit)
# Button 'Löschen'
btnDelete = Button()
btnDelete.text = 'Löschen'
btnDelete.bind(on_press=self.btn_delete)
self.top_layout.add_widget(btnCreate)
self.top_layout.add_widget(btnEdit)
self.top_layout.add_widget(btnDelete)
return self.main_layout
if __name__ == '__main__':
MyApp().run()
I added a GridLayout to a ScrollView, but this doesn't seem to work.
How can i make a scrollable list?
You have to set the size_hint_y of the GridLayout to None so that the height does not depend on the ScrollView and the size is minimum equal to the size of the GridLayout. On the other hand the Label must have size_hint_y to None so that the height does not depend on the GridLayout.
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
class MyApp(App):
main_layout = BoxLayout(orientation='vertical')
top_layout = BoxLayout(orientation='horizontal')
scrollView = ScrollView()
gridLayout = GridLayout(size_hint_y=None)
gridLayout.cols = 1
gridLayout.padding = [0, 0, 0, 0]
gridLayout.bind(minimum_height=gridLayout.setter('height'))
scrollView.add_widget(gridLayout)
main_layout.add_widget(top_layout)
main_layout.add_widget(scrollView)
def btn_create(self, instance):
self.gridLayout.add_widget(Label(text='test', size_hint_y=None))
def btn_edit(self, instance):
pass
def btn_delete(self, instance):
pass
def build(self):
self.top_layout.size_hint=(1, .1)
# Button 'Erstellen'
btnCreate = Button()
btnCreate.text = 'Erstellen'
btnCreate.bind(on_press=self.btn_create)
# Button 'Bearbeiten'
btnEdit = Button()
btnEdit.text = 'Bearbeiten'
btnEdit.bind(on_press=self.btn_edit)
# Button 'Löschen'
btnDelete = Button()
btnDelete.text = 'Löschen'
btnDelete.bind(on_press=self.btn_delete)
self.top_layout.add_widget(btnCreate)
self.top_layout.add_widget(btnEdit)
self.top_layout.add_widget(btnDelete)
return self.main_layout
if __name__ == '__main__':
MyApp().run()

Kivy - on_press option does not work within a "with self.canvas"?

It seems a lot of people have trouble with the on_press argument with Kivy, but I haven't found answers to my issue...
Here is what's happening:
I'm getting started with my first app in python/kivy. I know python, but perhaps not enough concerning classes... I am able to create a button, with a on_press action that opens a popup.
Now the goal is the following: I have a function affiche_grille, which displays a grid with lines on the screen. Inside each square, I create a button, with a text (a number). This works. I would like to bind an on_press event on these buttons : but now, the syntax does not work any more... Maybe is it because the button is created in a "with self.canvas" instruction, and the self.function is not appropriate any more ?
Here is the (complete after edit) code :
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.popup import Popup
from kivy.graphics import Color, Line
from kivy.core.window import Window
from kivy.core.text.text_layout import layout_text
from kivy.uix.floatlayout import FloatLayout
import numpy as np
from functools import partial # for on_press syntax
class Ecran_Principal(BoxLayout):
def build(self):
self.orientation='vertical'
self.liste_txt = np.zeros([9,9], dtype=object) # will contain Label
self.grille_affichee = np.zeros([9,9])
self.lGrille() # layout for the grid
self.lMenu() # layout for the menu
def lGrille(self):
LayGrille = GridLayout(cols=1,size_hint_y=0.8)
# window dimensions, in pixels
(L, H) = Window.size
H = int(0.8*H) # because of the 20% menu
if L > H: # landscape format - computer case
self.ymin = int(0.25*H) + int(0.05*H)
self.delta = int(0.1*H)
self.xmin = int((L-9*self.delta)/2.)
else: # portrait format - phone case
self.xmin = int(0.05*L)
self.delta = int(0.1*L)
self.ymin = int(0.25*H) + int((H-9*self.delta)/2.)
# end dimensions
self.deltaxrel = self.delta/H
self.deltayrel = self.delta/L
# grid definition (without numbers)
with self.canvas:
Color(1, 1, 1) # white
# automatic line traces
for i in range(4):
# big vertical lines
ymax = self.ymin+9*self.delta
xligne = self.xmin+i*3*self.delta
Line(points=[xligne, self.ymin, xligne, ymax], width=2)
# big horizontal lines
xmax = self.xmin+9*self.delta
yligne = self.ymin+i*3*self.delta
Line(points=[self.xmin, yligne, xmax, yligne], width=2)
# little intermediary lines
for ipetit in range(3):
if i ==3:
break
xpetit = xligne + ipetit*self.delta
Line(points=[xpetit, self.ymin, xpetit, ymax], width=1)
ypetit = yligne + ipetit*self.delta
Line(points=[self.xmin, ypetit, xmax, ypetit], width=1)
# end little lines
# end for
# grid display :
self.affiche_grille()
self.add_widget(LayGrille)
# end with
def affiche_grille(self):
# I tried to remove this 'with' instruction and does not change anything
with self.canvas:
for i in range(9): # line
for j in range(9): # colomn
valeur = self.grille_affichee[i,j]
val = "{0:.0f}".format(valeur)
layout = FloatLayout(size=(self.xmin + (j+0.5)*self.delta,
self.ymin + (8.5-i)*self.delta),
pos_hint=(None, None))
montexte = Button(text=val,
size_hint=(self.deltaxrel,
self.deltayrel),
pos=(self.xmin + (j+0.5)*self.delta,
self.ymin + (8.5-i)*self.delta),
background_color = (0,0.2,0,1),
background_normal = '',
on_press=partial(self.choisir_valeur, i, j)
)
self.liste_txt[i, j] = montexte
# THE BUTTONS AND THE TEXT ARE DISPLAYED,
# BUT NOTHING HAPPENS WHEN YOU PRESS THE BUTTONS
layout.add_widget(self.liste_txt[i, j])
# end j
# end i
# end with
def choisir_valeur(self, i, j):
print("Hello !") # NEVER DISPLAYED :(
#champ = TextInput(text=str(self.grille_affichee[i, j]),
# font_size=30,
# focus=True,
# multiline=False)
champ = Button(text=str(self.grille_affichee[i, j]))
popup = Popup(title='Value in line {} - colomn {}'.format(i, j),
content=champ,
size_hint=(0.5,0.5))
champ.bind(on_press=popup.dismiss)
popup.open()
def lMenu(self):
LayMenu = GridLayout(cols=2, rows=2, size_hint_y=0.2)
# Bouton Résoudre
self.BoutonResoudre=Button(text='Resoudre',size_hint=(0.5,0.1),pos_hint={'left': 0.},background_color=[0.9,0.9,0.9,1])
self.BoutonResoudre.bind(on_press=self.resoudre)
LayMenu.add_widget(self.BoutonResoudre)
# Bouton Remplir
self.BoutonScanner=Button(text='Scanner',size_hint=(0.5,0.1),pos_hint={'left': 0.5},background_color=[0.9,0.9,0.9,1])
self.BoutonScanner.bind(on_press=self.scanner)
LayMenu.add_widget(self.BoutonScanner)
# Bouton Indice
self.BoutonIndice=Button(text='Indice',size_hint=(0.5,0.1),pos_hint={'bottom': 0.},background_color=[0.9,0.9,0.9,1])
self.BoutonIndice.bind(on_press=self.indice)
LayMenu.add_widget(self.BoutonIndice)
# Bouton Quitter
self.BoutonQuitter=Button(text='Quitter',size_hint=(0.5,0.1),background_color=[0.9,0.9,0.9,1])
self.BoutonQuitter.bind(on_press=self.quitter)
LayMenu.add_widget(self.BoutonQuitter)
self.add_widget(LayMenu)
def resoudre(self, instance):
content = Button(text='Resolution du sudoku', font_size=20)
popup = Popup(title='RESOLUTION',content=content, size_hint=(0.5,0.5))
content.bind(on_press=popup.dismiss)
popup.open()
def scanner(self, instance):
content = Button(text='Remplissage auto par photo', font_size=20)
popup = Popup(title='SCAN PHOTO',content=content, size_hint=(0.5,0.5))
content.bind(on_press=popup.dismiss)
popup.open()
def indice(self, instance):
content = Button(text='Affichage d\'un indice', font_size=20)
popup = Popup(title='INDICE',content=content, size_hint=(0.5,0.5))
content.bind(on_press=popup.dismiss)
popup.open()
def quitter(self, instance):
content = Button(text='Au revoir !', font_size=20)
popup = Popup(title='QUITTER',content=content, size_hint=(0.5,0.5))
content.bind(on_press=exit())
popup.open()
class Sudoku(App):
def build(self):
ecran=Ecran_Principal()
ecran.build()
return ecran
if __name__ == '__main__':
Sudoku().run()
Everything is interpreted, but the buttons inside the grid are not working...
I've seen the functools.partial() tip, but it does not seem to be enough...
Does anyone have an idea of what is happening ? I am not very familiar with classes in python and I have certainly missed something.
Thank you in advance, and sorry if the question is too basic.
Well, you now know that you can't add a widget to canvas. Also, you shouldn't have a build method in your Ecran_Principal class. build() only belongs in the Sudoku() App class. Use __init__ instead.
I think if you try separating the visual stuff into kv language things will be easier. Below is an example utilizing spacing and padding with GridLayouts to 'draw' the game board. The buttons are hooked up with a simple callback.
btngrid.py
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.button import Button
class SmallGrid(GridLayout):
pass
class BigGrid(GridLayout):
pass
class GameBoard(AnchorLayout):
# A nice layout to use to keep things centered. Only one widget can be added to this.
def __init__(self, *args, **kwargs):
super(GameBoard, self).__init__(*args, **kwargs)
big = BigGrid()
for i in range(9):
small = SmallGrid()
for j in range(9):
small.add_widget(Button(text="{}, {}".format(i, j), on_release=self.callback))
big.add_widget(small)
self.add_widget(big)
def callback(self, button):
print button.text
class RootWidget(BoxLayout):
def __init__(self, *args, **kwargs):
super(RootWidget, self).__init__(*args, **kwargs)
self.orientation = 'vertical'
self.add_widget(GameBoard())
bottom_btns_container = GridLayout(cols=2, size_hint=(1, .2))
for i in range(4):
# Just for show, they don't do anything
bottom_btns_container.add_widget(Button(text="Button {}".format(i)))
self.add_widget(bottom_btns_container)
class BtnGridApp(App):
def build(self):
return RootWidget()
btngird.kv
<BigGrid>:
cols: 3
size_hint: (None, .8) # scales
width: self.height # scales
spacing: 5 # Width of lines
padding: 5 # perimeter border
# This draws a background for the whole grid.
# When used with spacing and padding, part of the background will show.
# Same with SmallGrid below
canvas.before:
Color:
rgba: [.9, .9, .9, 1]
Rectangle:
pos: self.pos
size: self.size
<SmallGrid>:
cols: 3
size_hint: (None, .8) # scales
width: self.height # scales
spacing: .25
canvas.before:
Color:
rgba: [.6, .6, .6, 1] # White lines
Rectangle:
pos: self.pos
size: self.size
<GameBoard>:
anchor_x: "center"
anchor_y: "center"

How to center text horizontally in a Kivy text input?

I want to center a single line of text in Kivy text input.
I'm going to use padding
widget.padding = [ (self.textinput.width - width of line) / 2, 20, 0, 0]
but i can't find the width of the line. How can I calculate or access the width of the line?
There is an internal TextInput._get_text_width method you can use to calculate proper padding:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
Builder.load_string('''
<MyWidget>:
TextInput:
multiline: False
on_text: root.update_padding(args[0])
padding_x: self.width/2 # initial padding
''')
class MyWidget(FloatLayout):
def update_padding(self, text_input, *args):
text_width = text_input._get_text_width(
text_input.text,
text_input.tab_width,
text_input._label_cached
)
text_input.padding_x = (text_input.width - text_width)/2
class MyApp(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
MyApp().run()
The solution above almost worked for me. Sometimes the padding wouldn't be updated correctly. Here is my slight change, setting text_width as a NumericProperty:
In Kivy:
<CenteredTextInput#TextInput>:
multiline: False
on_text: root.update_padding()
padding_x: (self.width - self.text_width) / 2
In Python:
class CenteredTextInput(TextInput):
'''
A centered TextInput.
'''
text_width = NumericProperty()
'''The text width
'''
def update_padding(self, *args):
'''
Update the padding so the text is centered
'''
self.text_width = self._get_text_width(
self.text,
self.tab_width,
self._label_cached
)
You could make a textinput behind a button, and make the button visualize as the text input.
When pushing the button, put the focus to the textinput, and update the buttons text.
I have made an example here.
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.app import App
from kivy import require
require('1.9.1')
class MyWidget(BoxLayout):
def __init__(self,**kwargs):
super(MyWidget,self).__init__(**kwargs)
self.orientation = "vertical"
self.cur = False
self.textinput = TextInput(text='',halign="center",multiline=False)
self.textinput.bind(text=self.on_text)
self.button = Button(background_normal="",background_color=[0,0,0.1,1],font_size="40sp")
self.button.bind(on_release=self.button_click)
self.my_float_layout = FloatLayout()
self.my_float_layout.add_widget(self.textinput)
self.my_float_layout.add_widget(self.button)
self.add_widget(Label(text="type text below",font_size="40sp"))
self.add_widget(self.my_float_layout)
Clock.schedule_interval(self.cursor, 0.5)
def cursor(self,dt): # function to visualize a cursor
if self.textinput.focus:
cur_pos = self.textinput.cursor[0]
if not self.cur:
self.button.text = self.textinput.text[:cur_pos] + "|" + self.textinput.text[cur_pos:]
self.cur = True
else:
self.button.text = self.textinput.text[:cur_pos] + " " + self.textinput.text[cur_pos:]
self.cur = False
elif self.cur:
self.button.text = self.textinput.text + " "
self.cur = False
def on_text(self, *args): # function to set the button text
self.button.text = self.textinput.text
def button_click(self,*args): # function to focus the input
self.textinput.focus = True
class MyApp(App):
def build(self):
return MyWidget()
if __name__ == "__main__":
MyApp().run()

Categories