I'm coding an application in Kivy (seriously impressive library).
I'm coming up against an interesting issue, and wondering if I'm missing something and/or if there's a fix.
I created some buttons in the app (I've actually converted to an apk and installed it on my phone and this is a screenshot from there, but the same thing happens on the emulator).
You'll notice that the word "Random" breaks into two lines with "Rando" and "m".
The buttons' text_size is linked to the buttons's size property, so the code for the text was like this:
for button in self.button_layout.children:
button.text_size = button.size
button.halign = 'center'
button.valign = 'middle'
From my perspective, this should have allowed "Random" and "Breadth" to have fitted on one line quite easily. I can only guess that there's a race condition when it does the layout.
When I run
>>python main.py
And size it similar to the phone size, it looks like this (which is what I want):
What can I do to make sure that it renders the screen like this? Any common traps that I might be missing.
Minimum Working Example:
#__version__ = '1.0'
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
class ButtonTest(BoxLayout):
def __init__(self, **kwargs):
super(ButtonTest, self).__init__(**kwargs)
self.drawGui()
def drawGui(self,instance=None):
self.clear_widgets()
self.orientation = 'vertical'
#self.define_square_positions()
# Buttons
self.prac_button_layout = GridLayout(cols=3)
prac_random = Button(text="Random")
prac_depth_first = Button(text="Depth first")
prac_breadth_first = Button(text="Breadth first")
self.prac_button_layout.add_widget(prac_random)
self.prac_button_layout.add_widget(prac_breadth_first)
self.prac_button_layout.add_widget(prac_depth_first)
self.add_widget(self.prac_button_layout)
for button in self.prac_button_layout.children:
button.text_size = button.size
button.halign = 'center'
button.valign = 'middle'
def on_size(self, *args):
self.drawGui()
class MyApp(App):
def build(self):
return ButtonTest()
if __name__ == '__main__':
MyApp().run()
The intent is that the text in the button more or less fills the button (ie wraps when necessary but is able to take the full width of the button), and that this adjusts dynamically when re-sized.
If I run
>python main.py
it starts like this (which suggests it's already not working here actually):
But I simply moved this from one screen to another and it goes like this:
And when I resize it, it behaves reasonably well:
However, when I create the apk (bulldozer android) and open it in an emulator (Android Studios, Pixel 9 emulator in this case), it looks like this:
So the question is how do I make a set of simple buttons where the text wraps if it's too wide, but fills the button's width and height and is robust against dynamic resizing?
The problem is that in your drawGui()
method the lines:
for button in self.prac_button_layout.children:
button.text_size = button.size
button.halign = 'center'
button.valign = 'middle'
are setting the text_size
to the button size
before the button size
has been set to its final value. When the above code is executed, the button size
is still at its default value of (100, 100)
, so the text_size
is always getting set to that value. Since you are re-creating the buttons each time that drawGui()
, that default value is set every time. One way to work around this is to delay the execution of the above code until the button size
is finally set. You can do this by modifying your drawGui()
code like this:
def drawGui(self, instance=None):
self.clear_widgets()
self.orientation = 'vertical'
# self.define_square_positions()
# Buttons
self.prac_button_layout = GridLayout(cols=3)
prac_random = Button(text="Random")
prac_depth_first = Button(text="Depth first")
prac_breadth_first = Button(text="Breadth first")
self.prac_button_layout.add_widget(prac_random)
self.prac_button_layout.add_widget(prac_breadth_first)
self.prac_button_layout.add_widget(prac_depth_first)
self.add_widget(self.prac_button_layout)
Clock.schedule_once(self.set_text_size)
def set_text_size(self, _dt):
for button in self.prac_button_layout.children:
button.text_size = button.size
button.halign = 'center'
button.valign = 'middle'
This delays the text_size
setting.
Note that setting the text_size
in python does not mean that the text_size
will be modified when the button size
changes. Using the kv
language to set up your GUI, could automatically adjust text_size
and you would not need to re-create it every time the size
of the ButtonTest
object changes.