Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I set minimum allowable width/height for widget/layout in Kivy?

I have BoxLayout with 3 elements, I need that first and last elements to occupy minimum available space. Middle element has fixed proportion (1:1), so when I resize the window, side elements become too small and content go out of it. I need e.g. label's (or button's, or even set's of different elements) text to be always inside the label. This size shouldn't be more, so I can say it should be fixed size depending on its content.

UPDATE: I've made a mistake, size can be more, but not less. How it should be then?

UPDATE: So it's my BoxLayout: enter image description here When I expand the window, only side parts should expand: enter image description here And when I constrict the window, side parts should have some minimum size: enter image description here So it's kind of fixed minimum I think.

like image 384
Necronomicron Avatar asked Jul 17 '14 12:07

Necronomicron


1 Answers

Content size of Label is avaiable through texture_size property, so you can set size_hint to None and bind size to content size:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button 

from kivy.lang import Builder

kv = '''
<MyButton>:
    size_hint: None, 1
    size: self.texture_size
'''
Builder.load_string(kv)

class MyButton(Button):
    pass

class MyWidget(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.add_widget(MyButton(text="Fixed size button"))
        self.add_widget(Button(text="Normal button"))
        self.add_widget(MyButton(text="Fixed size button"))         

class MyApp(App):
    def build(self):
        return MyWidget()

if __name__ == '__main__':
    MyApp().run()

You should also check text_size property. From documentation: "By default, the label is not constrained to any bounding box. You can set the size constraint of the label with this property. The text will autoflow into the constrains. So although the font size will not be reduced, the text will be arranged to fit into the box as best as possible, with any text still outside the box clipped". For example:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button 

from kivy.lang import Builder

kv = '''
<MyButton>:
    text_size: self.size
    valign: "middle"
    halign: "center"
'''
Builder.load_string(kv)

class MyButton(Button):
    pass

class MyWidget(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.add_widget(MyButton(text="Example text which is too long to fit in one line"))
        self.add_widget(Button(text="Normal button"))
        self.add_widget(MyButton(text="Example text which is too long to fit in one line"))

class MyApp(App):
    def build(self):
        return MyWidget()

if __name__ == '__main__':
    MyApp().run()

UPDATE If you want even more control about the way widgets scale, you can create method which calculate values for widgets and have it binded to changing size property (either bind or implementing on_size()). For example:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.uix.label import Label

from kivy.lang import Builder

from functools import partial

kv = '''
<CentralWidget>:
    pos_hint: {'center_y': .5} 
    size_hint: None, None
    canvas:
        Color:
            rgb: 1, 0, 1
        Rectangle:
            pos: self.pos
            size: self.size

<SideWidget>:
    pos_hint: {'center_y': .5} 
    size_hint: None, 1
    canvas.before:
        Color:
            rgb: 0, 0, 1
        Rectangle:
            pos: self.pos
            size: self.size
'''

Builder.load_string(kv)

class CentralWidget(Widget):
    pass

class SideWidget(Label):
    pass

class MyWidget(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        w1 = SideWidget(text="............................")
        w2 = CentralWidget()
        w3 = SideWidget(text="............................")

        self.add_widget(w1)
        self.add_widget(w2)
        self.add_widget(w3)

    def on_size(self, *args):
        # self.size - size of parent widget
        # self.children - children of widget
        # self.children[0].texture_size - sife of content of selectend children widget
        # self.children[0].size - size of selected children widget to set
        if((self.size[0]-500)/2 > self.children[0].texture_size[0]):
            self.children[0].size = ((self.size[0]-500)/2, 0)     
            self.children[1].size = (500, 500)   
            self.children[2].size = ((self.size[0]-500)/2, 0)   
        else:
            self.children[1].size = (self.size[0]-2*self.children[0].texture_size[0], self.size[0]-2*self.children[0].texture_size[0])  

class MyApp(App):
    def build(self):
        return MyWidget()

if __name__ == '__main__':
    MyApp().run()
like image 167
Nykakin Avatar answered Sep 21 '22 03:09

Nykakin