KivyMD: Automatic resizing of MDBoxLayout and MDLabel based on text length
Question:
I’m trying to make a simple chat that auto-sizes the message box to fit text of different lengths.
For some reason, when the text is not very long, everything works well. But with increasing text length in the field and its transition to the second line, the program doesn’t work correctly.
I know that the problem is in adaptive_height
of MDBoxlayout
and MDLabels
, and conflicts between them, but I don’t know how to make this work without it, and what’s going on in specific, when algorithms go crazy.
main.py:
from kivy.config import Config
Config.set('graphics', 'resizable', 0)
Config.set("graphics", "width", 360)
Config.set("graphics", "height", 740)
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.properties import BooleanProperty, StringProperty, ListProperty
from kivymd.uix.boxlayout import MDBoxLayout
class MesApp(MDApp):
chat_logs = ListProperty()
def build(self):
return Builder.load_file('main.kv')
def send(self, text):
self.chat_logs.append(
{"text": text if text else 'empty message',
"send_by_user": True,
"time": "21.10.2022 13:11",
"pos_hint": {"right": 1},
}
)
self.receive()
def receive(self):
self.chat_logs.append(
{
"text": 'Answer',
"time": '21.10.2022 14:30',
"send_by_user": False,
}
)
Builder.load_string(
"""
<Chat>:
adaptive_height: True
padding: [dp(8), dp(8)]
orientation: 'vertical'
canvas.before:
Color:
rgba:
app.theme_cls.primary_light if self.send_by_user
else app.theme_cls.primary_dark
RoundedRectangle:
size: self.size
pos: self.pos
radius:
[dp(8), dp(8), (dp(-5), dp(5)), dp(8)] if self.send_by_user
else [(dp(-5), dp(5)), dp(8), dp(8), dp(8)]
MDLabel:
text: root.text
adaptive_height: True
MDLabel:
text: root.time
font_size: '12sp'
theme_text_color: 'Secondary'
adaptive_height: True
""")
class Chat(MDBoxLayout):
send_by_user = BooleanProperty()
time = StringProperty()
text = StringProperty()
if __name__ == '__main__':
MesApp().run()
main.kv:
<ScreenM>:
MDBoxLayout:
orientation: 'vertical'
padding: dp(5), dp(5)
RecycleView:
data: app.chat_logs
viewclass: 'Chat'
RecycleBoxLayout:
padding: dp(10)
spacing: dp(15)
orientation: "vertical"
size_hint_y: None
height: self.minimum_size[1]
default_size_hint: 0.8, None
default_size: None, None
TextInput:
id: field
hint_text: "Write your message"
multiline: True
cursor_color: app.theme_cls.primary_color
padding: dp(7)
size_hint_y: None
height: dp(35)
MDIconButton:
icon: "send-circle-outline"
# user_font_size: '50'
pos_hint: {"center_x": .5}
icon_size: "50sp"
on_release: app.send(field.text)
Some of the warnings:
[CRITICAL] [Clock ] Warning, too much iteration done before the next frame. Check your code, or increase the Clock.max_iteration attribute. Remaining events:
<ClockEvent (-1.0) callback=<bound method BoxLayout.do_layout of <__main__.Chat object at 0x0000029E9552ABA0>>>
<ClockEvent (-1.0) callback=<bound method BoxLayout.do_layout of <__main__.Chat object at 0x0000029E95594BA0>>>
<ClockEvent (-1.0) callback=<bound method Label.texture_update of <kivymd.uix.label.label.MDLabel object at 0x0000029E955A05F0>>>
<ClockEvent (-1.0) callback=<bound method RecycleViewBehavior.refresh_views of <kivy.uix.recycleview.__init__.RecycleView object at 0x0000029E92E3A6D0>>>
kivy==2.1.0
kivymd==1.1.1
Answers:
There seems to be an interaction between the MDBoxLayout
and the MDLabels
within it causing an endless loop. You can eliminate the adaptive_height
from the Chat
rule with a simple calculation of its height. Here is a modified version of that <Chat>
rule:
<Chat>:
size_hint_y: None
height: lab1.height + lab2.height + self.padding[1] + self.padding[3] + self.spacing # calculate height
padding: [dp(8), dp(8)]
orientation: 'vertical'
canvas.before:
Color:
rgba:
app.theme_cls.primary_light if self.send_by_user
else app.theme_cls.primary_dark
RoundedRectangle:
size: self.size
pos: self.pos
radius:
[dp(8), dp(8), (dp(-5), dp(5)), dp(8)] if self.send_by_user
else [(dp(-5), dp(5)), dp(8), dp(8), dp(8)]
MDLabel:
id: lab1 # id added
text: root.text
adaptive_height: True
MDLabel:
id: lab2 # id added
text: root.time
font_size: '12sp'
theme_text_color: 'Secondary'
adaptive_height: True
I’m trying to make a simple chat that auto-sizes the message box to fit text of different lengths.
For some reason, when the text is not very long, everything works well. But with increasing text length in the field and its transition to the second line, the program doesn’t work correctly.
I know that the problem is in adaptive_height
of MDBoxlayout
and MDLabels
, and conflicts between them, but I don’t know how to make this work without it, and what’s going on in specific, when algorithms go crazy.
main.py:
from kivy.config import Config
Config.set('graphics', 'resizable', 0)
Config.set("graphics", "width", 360)
Config.set("graphics", "height", 740)
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.properties import BooleanProperty, StringProperty, ListProperty
from kivymd.uix.boxlayout import MDBoxLayout
class MesApp(MDApp):
chat_logs = ListProperty()
def build(self):
return Builder.load_file('main.kv')
def send(self, text):
self.chat_logs.append(
{"text": text if text else 'empty message',
"send_by_user": True,
"time": "21.10.2022 13:11",
"pos_hint": {"right": 1},
}
)
self.receive()
def receive(self):
self.chat_logs.append(
{
"text": 'Answer',
"time": '21.10.2022 14:30',
"send_by_user": False,
}
)
Builder.load_string(
"""
<Chat>:
adaptive_height: True
padding: [dp(8), dp(8)]
orientation: 'vertical'
canvas.before:
Color:
rgba:
app.theme_cls.primary_light if self.send_by_user
else app.theme_cls.primary_dark
RoundedRectangle:
size: self.size
pos: self.pos
radius:
[dp(8), dp(8), (dp(-5), dp(5)), dp(8)] if self.send_by_user
else [(dp(-5), dp(5)), dp(8), dp(8), dp(8)]
MDLabel:
text: root.text
adaptive_height: True
MDLabel:
text: root.time
font_size: '12sp'
theme_text_color: 'Secondary'
adaptive_height: True
""")
class Chat(MDBoxLayout):
send_by_user = BooleanProperty()
time = StringProperty()
text = StringProperty()
if __name__ == '__main__':
MesApp().run()
main.kv:
<ScreenM>:
MDBoxLayout:
orientation: 'vertical'
padding: dp(5), dp(5)
RecycleView:
data: app.chat_logs
viewclass: 'Chat'
RecycleBoxLayout:
padding: dp(10)
spacing: dp(15)
orientation: "vertical"
size_hint_y: None
height: self.minimum_size[1]
default_size_hint: 0.8, None
default_size: None, None
TextInput:
id: field
hint_text: "Write your message"
multiline: True
cursor_color: app.theme_cls.primary_color
padding: dp(7)
size_hint_y: None
height: dp(35)
MDIconButton:
icon: "send-circle-outline"
# user_font_size: '50'
pos_hint: {"center_x": .5}
icon_size: "50sp"
on_release: app.send(field.text)
Some of the warnings:
[CRITICAL] [Clock ] Warning, too much iteration done before the next frame. Check your code, or increase the Clock.max_iteration attribute. Remaining events:
<ClockEvent (-1.0) callback=<bound method BoxLayout.do_layout of <__main__.Chat object at 0x0000029E9552ABA0>>>
<ClockEvent (-1.0) callback=<bound method BoxLayout.do_layout of <__main__.Chat object at 0x0000029E95594BA0>>>
<ClockEvent (-1.0) callback=<bound method Label.texture_update of <kivymd.uix.label.label.MDLabel object at 0x0000029E955A05F0>>>
<ClockEvent (-1.0) callback=<bound method RecycleViewBehavior.refresh_views of <kivy.uix.recycleview.__init__.RecycleView object at 0x0000029E92E3A6D0>>>
kivy==2.1.0
kivymd==1.1.1
There seems to be an interaction between the MDBoxLayout
and the MDLabels
within it causing an endless loop. You can eliminate the adaptive_height
from the Chat
rule with a simple calculation of its height. Here is a modified version of that <Chat>
rule:
<Chat>:
size_hint_y: None
height: lab1.height + lab2.height + self.padding[1] + self.padding[3] + self.spacing # calculate height
padding: [dp(8), dp(8)]
orientation: 'vertical'
canvas.before:
Color:
rgba:
app.theme_cls.primary_light if self.send_by_user
else app.theme_cls.primary_dark
RoundedRectangle:
size: self.size
pos: self.pos
radius:
[dp(8), dp(8), (dp(-5), dp(5)), dp(8)] if self.send_by_user
else [(dp(-5), dp(5)), dp(8), dp(8), dp(8)]
MDLabel:
id: lab1 # id added
text: root.text
adaptive_height: True
MDLabel:
id: lab2 # id added
text: root.time
font_size: '12sp'
theme_text_color: 'Secondary'
adaptive_height: True