Kivy MDBoxLayout keeps increasing width automatically when running, raised [CRITICAL] [Clock] Warning

Viewed 210

When I add a ChatBubble in my app, that ChatBubble will automatically increase its width for each millisecond although its size is already given.

The issue shown in the command prompt is

[CRITICAL] [Clock ] Warning, too much iteration done before the next frame. Check your code, or increase the Clock.max_iteration attribute

I'm trying to find a way to make ChatBubble work normally like any other chatbox like Messenger or Whatsapp, I also tried to find the problem in my code for hours but couldn't. Any help would be appreciated.

I converted the part of the problem code into a small app for you to see the issue easier.

.py file

from kivy.lang import Builder
from kivy.properties import BooleanProperty, DictProperty, OptionProperty, StringProperty
from kivymd.app import MDApp 
#from components.label import PLabel
from kivymd.uix.boxlayout import MDBoxLayout, BoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.screenmanager import Screen
from kivy.uix.stacklayout import StackLayout
from kivy.uix.button import Button

class ChatBubble(MDBoxLayout):
    send_by_user = BooleanProperty()
    msg = StringProperty()
    time = StringProperty()

class Container(MDBoxLayout):
    def on_button_click(self):
        self.on_start()
    def on_start(self):
        self.c = ChatBubble()
        self.c.send_by_user = True
        self.c.msg = "hello world"
        self.c.time = "11:22AM"
        self.ids["lst"].add_widget(self.c)
        
class MainApp(MDApp):
    def build(self):
        self.root = Builder.load_file("chat_bubble.kv")

if __name__ == "__main__":
    MainApp().run()

.kv file

Container:
 
<Container>:
    orientation: "vertical"
    ScrollView:
        MDList:
            id: lst
            spacing: 5
            padding: [0, 10, 0, 10]
    Button:
        text: "Click"
        on_press:
            root.on_button_click()

<ChatBubble>:
    id: chtbld
    md_bg_color: [0, 0, 0, 0]
    size_hint_y: None
    height: 60
    adaptive_height: True
    width: root.width
    padding: [10, 0, 10, 0]
    orientation: 'vertical'

    MDBoxLayout:
        height: msg_content.height + timed.height + 10
        width: msg_content.width + wid1.width + wid2.width
        size_hint: None, None
        pos_hint: {'right': 1} if chtbld.send_by_user == True else {'left': 1}
        radius: [10, 10, (1, -5), 10] if self.pos_hint == {'right': 1} else [10, 10, 10, (1, -5)]
        md_bg_color: app.theme_cls.primary_dark if root.send_by_user else app.theme_cls.primary_color
        Spacer:
            id: wid1
        MDBoxLayout:
            orientation: 'vertical'
            height: msg_content.height + tc.height + wid3.height
            width: msg_content.width

            MDLabel:
                id: msg_content
                text: root.msg
                width: tc.width if self.texture_size[0] < tc.width else self.texture_size[0]
                height: self.texture_size[1]
                size_hint_y: None
                text_size: chtbld.width-30 if self.width >= chtbld.width-30 else None, None
                halign: 'left'
                color: app.theme_cls.opposite_bg_normal

            MDBoxLayout:
                id: tc
                size_hint_y: None
                height: timed.height
                width: timed.width + 3
                spacing: 3
                MDLabel:
                    id: timed
                    text: root.time
                    size: self.texture_size
                    size_hint_y: None
                    font_size: 9
                    bold: True
                    halign: 'right'
                    text_size: None, None
                    color: [.8, .8, .8, 1]
            Spacer:
                id: wid3
                height: 5
        Spacer:
            id: wid2

<Spacer@Widget>:
    id: wid
    width: 5
    size_hint: None, None
2 Answers

That kind of behavior usually means that the sizing of your widgets is getting into an infinite loop of inter-dependent widget sizes. You can stop that loop by changing the width calculation of your MDLabel with the id of msg_content from:

width: tc.width if self.texture_size[0] < tc.width else self.texture_size[0]

to:

width: self.texture_size[0]

This may not be what you want, but it seems that the dependency of msg_content ontc is causing the looping.

I believe the problem is due to the inter-dependencies of inner widgets (especially the MDLabel with id: msg_content, MDBoxLayout with id: tc, MDLabel with id: timed) in the field of size, texture_size etc. and not handling those properties separately.

Disabling the size_hint before setting custom size seems to work.

            ...
            MDBoxLayout:
                id: tc
                size_hint: None, None # To set custom size.
                height: timed.height
                width: timed.width + 3
                pos_hint: {'right': 1} # To hold itself and text on right.
                spacing: 3
                MDLabel:
                    id: timed
                    text: root.time
                    size_hint: None, None # To set custom size.
                    size: self.texture_size
                    font_size: 9
                    bold: True
                    halign: 'right' # This wouldn't work since its size is set to its texture_size.
                    ...

You should also change the pos_hint value as {'left': 1} (which is an invalid key, may be {"x" : 0} is what you want).


However I found your design somewhere over complicated. There's a sample example (messenger.py) by one of the kivy's core developer tshirtman, you may look into.

Related