Search code examples
pythonkivykivymd

Kivy: Updating Label in several widgets and screens


I'm trying to update the value of a label in 2 widgets and a screen. This should be pretty straightforward but I haven't been able to make it work!

I suspect that whatever I'm doing is not being picked up by the GUI for some reason. I say this because when "printing values" to debug the value does update as expected.

Finally, I've looked at several solutions but neither seem to work: Kivy Label Text does not update, Kivy: Label text does not update during for-loop, Kivy Label.text Property doesn't update on the UI

The screen I'm trying to update:

Screens/recipe_screen.py

from kivymd.uix.screen import MDScreen

class RecipeScreen(MDScreen):
    pass

Screens/recipe_screen.kv

#:import CounterWidget Components.counter_widget.CounterWidget
#:import IngredientsLabel Components.ingredients_label.IngredientsLabel

<RecipeScreen>:
    name: 'recipe_screen'
    id: recipe_screen

    MDBoxLayout:
        adaptive_height: True
        orientation: 'horizontal'
        padding: 30, 20
        spacing: 0

        IngredientsLabel:
            id: il
            label_text: 'text I'm trying to update'

        CounterWidget:

Widget 1: this is what I'm calling from the GUI

components/counter_widget.py

from kivymd.uix.card import MDCard
from Components.ingredients_label import IngredientsLabel


class CounterWidget(MDCard):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.counter = 4

    def increase_widget(self):
        self.counter += 1
        self.ids['counter_text'].text = str(self.counter) <--- THIS WORKS FINE
        ingredient_label_ref = IngredientsLabel
        ingredient_label_ref().change_label(self.counter) <--- THIS DOES NOT WORK

components/counter_widget.kv

#:import RecipeScreen Screens.recipe_screen.RecipeScreen

<CounterWidget>:

    elevation: 5
    border_radius: 15
    radius: [15]
    size_hint: None, None
    size: 250, 90

    MDGridLayout:

        MDLabel:
            id: counter_text
            text: '4'

Widget 2: this is the widget I'm trying to update based on Widget 1

components/ingredients_label.py

from kivymd.uix.boxlayout import MDBoxLayout

class IngredientsLabel(MDBoxLayout):

    def change_label(self, counter):
        self.ids['servings_counter'].text = str(counter) + ' servings' <--- DOES NOT UPDATE GUI
        print('IngredientsLabel: ' + str(counter) + ' servings')       <--- PRINTS CORRECTLY IN TERMINAL

components/ingredients_label.kv

#:import RecipeScreen Screens.recipe_screen.RecipeScreen

<IngredientsLabel>:

    adaptive_height: True
    orientation: 'vertical'

    MDLabel:
        id: servings_counter
        markup: True
        text: '4 servings'
        font_style: 'Subtitle2'

main.py

from kivy.core.window import Window
from kivymd.app import MDApp
from kivy.factory import Factory
from kivy.uix.screenmanager import ScreenManager

from Screens.recipe_screen import RecipeScreen
from Screens.carousel_screen import CarouselScreen

class MainApp(App, MDApp):

    def build_app(self):
        Window.size = [350, 560]
        sm = ScreenManager()
        sm.add_widget(RecipeScreen(name='recipe_screen'))
        sm.add_widget(CarouselScreen(name='carousel_screen'))
        return sm

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

Update post @John Anderson suggestion to:

def increase_widget(self):
    self.counter += 1
    self.ids['counter_text'].text = str(self.counter)
    ingredient_label_ref = MDApp.get_running_app().root.get_screen('recipe_screen').ids.il  # get reference to IngredientsLabel
    ingredient_label_ref.change_label(self.counter)

Unfortunately, this didn't work either.

I also tried referencing in the .kv file via:

Changing the components/ingredients_label.kv to

#:import RecipeScreen Screens.recipe_screen.RecipeScreen

<IngredientsLabel>:

    adaptive_height: True
    orientation: 'vertical'
    label_text: 'None'

    MDLabel:
        markup: True
        text:'Ingredients for'
        font_style: 'H6'

    MDLabel:
        markup: True
        text: root.label_text
        font_style: 'Subtitle2'

and components/counter_widget.py to (while keeping the 'il' id reference for the IngredientsLabel widget on the RecipeScreen)

from kivymd.uix.card import MDCard
from Components.ingredients_label import IngredientsLabel


class CounterWidget(MDCard):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.counter = 4

    def increase_widget(self):
        self.counter += 1
        self.ids['il'].label_text = str(self.counter)

but this didn't work either (no error, but no update to the UI either...).

I included the App class above as well.


Solution

  • Without a complete runnable code (you have not provided an App class), it is difficult to provide a definitive answer. However, assuming that your App creates a ScreenManager with RecipeScreen as one of its Screens, you can modify your increase_widget() method:

    def increase_widget(self):
        self.counter += 1
        self.ids['counter_text'].text = str(self.counter)
        ingredient_label_ref = MDApp.get_running_app().root.get_screen('recipe_screen').ids.il  # get reference to IngredientsLabel
        ingredient_label_ref.change_label(self.counter)
    

    In order for this to work, you must add some ids to your kv:

    <RecipeScreen>:
        name: 'recipe_screen'
        id: recipe_screen
    
        MDBoxLayout:
            adaptive_height: True
            orientation: 'horizontal'
            padding: 30, 20
            spacing: 0
    
            IngredientsLabel:
                id: il  # added id
    
            CounterWidget:
                id: cw  # added, but not required