'How to use a variable/function from a class instance in a differrent class with kivy?

first, I'm new here, so don't hesitate to correct me if I act innapropriatly !

I'm making a sudo turn based RPG combat to train with kivy tools and I've come in contact with a big problem, I'm having a lot of troubles making a kivy classe use variables and function from another kivy class instance, for some case I used self.parent.parent.parent etc but It is very frail and unsatisfying, and it does not work everywhere, I know that in theory I should use id and/or app but I just can't make it work, I guess I'm missing something with how they work, here is my python file :

import kivy
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.widget import Widget
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.uix.screenmanager import ScreenManager, Screen, SlideTransition, NoTransition, CardTransition, SwapTransition, \
    FadeTransition, WipeTransition, FallOutTransition, RiseInTransition
from kivy.properties import StringProperty, NumericProperty, ObjectProperty, BooleanProperty
from kivy.core.audio import SoundLoader
import time

game_won = False
game_over = False
disabled_time = 1.5


class MainWidget(BoxLayout):
    Ennemy_img = StringProperty("images/slime.png")
    Effect_img = StringProperty("images/void.png")
    menu_widget = ObjectProperty()
    menu_size = NumericProperty(1)
    menu_size_x = NumericProperty(1)
    monster_hp_lost = NumericProperty(0)
    monster_hp = NumericProperty(100)
    monster_hp_left_ratio = NumericProperty(1)
    monster_xp_gain = NumericProperty(20)
    hero_hp_lost = NumericProperty(0)
    hero_hp = NumericProperty(100)
    hero_mp_lost = NumericProperty(0)
    hero_mp = NumericProperty(100)
    hero_xp = NumericProperty(0)
    hero_lvl = NumericProperty(1)
    next_level = NumericProperty(100)
    hp_left_ratio = NumericProperty(1)
    monster_attack_effect = StringProperty("images/void.png")
    ennemy_turn = NumericProperty(0)
    object_1_qty = NumericProperty(3)
    object_2_qty = NumericProperty(3)
    object_3_qty = NumericProperty(3)



    spit_sound = None
    lvl_up_sound = None
    fizzle_sound = None
    game_over_sound = None
    sword_sound = None
    fire_sound = None
    wind_sound = None
    heal_sound = None
    victory_sound = None

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.init_audio()

    def init_audio(self):
        self.spit_sound = SoundLoader.load("audio/spit.wav")
        self.lvl_up_sound = SoundLoader.load("audio/lvlup.wav")
        self.fizzle_sound = SoundLoader.load("audio/Fizzle.wav")
        self.sword_sound = SoundLoader.load("audio/sword.wav")
        self.fire_sound = SoundLoader.load("audio/fireball.wav")
        self.wind_sound = SoundLoader.load("audio/tornado.wav")
        self.heal_sound = SoundLoader.load("audio/healing.wav")
        self.victory_sound = SoundLoader.load("audio/victory.wav")
        self.game_over_sound = SoundLoader.load("audio/Game_over.wav")

        self.spit_sound.volume = 3
        self.lvl_up_sound.volume = 3
        self.sword_sound.volume = 5
        self.fire_sound.volume = 3
        self.wind_sound.volume = 3
        self.heal_sound.volume = 5
        self.victory_sound.volume = 1
        self.game_over_sound.volume = 5

    def reset_all(self):
        self.Ennemy_img = "images/slime.png"
        self.Effect_img = "images/void.png"
        self.ids.Effect_spell.source = "images/void.png"
        self.monster_attack_effect = "images/void.png"
        self.menu_size = 1
        self.menu_size_x = 1
        self.monster_hp_lost = 0
        self.monster_hp = 100
        self.monster_hp_left_ratio = 1
        self.hero_hp_lost = 0
        self.hero_hp = 100
        self.hero_mp = 100
        self.hero_mp_lost = 0
        self.hp_left_ratio = 1
        self.ennemy_turn = 0

    def spell_effect_reset(self):
        self.ids.Effect_spell.source = "images/void.png"
        self.monster_attack_effect = "images/void.png"

    def check_lifes_totals(self):
        global game_won
        global game_over
        if self.monster_hp_lost >= self.monster_hp:
            self.victory_sound.play()
            game_won = True
            self.hero_xp += self.monster_xp_gain
            if self.hero_xp >= self.next_level:
                self.hero_lvl += 1
                self.hero_xp = 0
                self.lvl_up_sound.play()
            self.reset_all()
        elif self.hero_hp_lost >= self.hero_hp:
            game_over = True
            self.reset_all()

    def monster_turn(self, dt):
        self.ids.Effect_spell.source = "images/void.png"
        self.hero_hp_lost += 10
        self.spit_sound.play()
        self.monster_attack_effect = "images/slime_spit.png"
        self.ennemy_turn = 0

    def on_attack(self):
        self.ennemy_turn = 1
        self.spell_effect_reset()
        self.sword_sound.play()
        self.ids.Effect_spell.source = "images/slash.png"
        self.monster_hp_lost += 10
        self.check_lifes_totals()
        Clock.schedule_once(self.monster_turn, disabled_time)

    def on_spell_fire_pressed(self):
        self.spell_effect_reset()
        mp_cost = 35
        if self.hero_mp - self.hero_mp_lost >= mp_cost:
            self.ennemy_turn = 1
            self.fire_sound.play()
            self.ids.Effect_spell.source = "images/fireball.png"
            self.monster_hp_lost += 30
            self.hero_mp_lost += 30
            self.check_lifes_totals()
            # self.monster_hp_left_ratio = (self.monster_hp - self.hp_lost) / self.monster_hp
            Clock.schedule_once(self.monster_turn, disabled_time)

        else:
            self.fizzle_sound.play()

    def on_spell_wind_pressed(self):
        self.spell_effect_reset()
        mp_cost = 20
        if self.hero_mp - self.hero_mp_lost >= mp_cost:
            self.ennemy_turn = 1
            self.wind_sound.play()
            self.ids.Effect_spell.source = "images/tornado.png"
            self.hero_hp_lost += 20
            self.hero_mp_lost += 20
            self.check_lifes_totals()
            Clock.schedule_once(self.monster_turn, disabled_time)
        else:
            self.fizzle_sound.play()

    def on_spell_heal_pressed(self):
        self.spell_effect_reset()
        mp_cost = 20
        if self.hero_mp - self.hero_mp_lost >= mp_cost:
            self.ennemy_turn = 1
            self.heal_sound.play()
            self.monster_attack_effect = "images/heal.png"
            self.hero_hp_lost -= 20
            self.hero_mp_lost += mp_cost
            self.check_lifes_totals()
            Clock.schedule_once(self.monster_turn, disabled_time)
        else:
            self.fizzle_sound.play()

    def on_object_1_pressed(self):
        self.spell_effect_reset()
        if self.object_1_qty > 0:
            self.ennemy_turn = 1
            self.heal_sound.play()
            self.monster_attack_effect = "images/heal_potion.png"
            self.hero_hp_lost -= 30
            self.object_1_qty -= 1
            self.check_lifes_totals()
            Clock.schedule_once(self.monster_turn, disabled_time)
        else:
            self.fizzle_sound.play()

    def on_object_2_pressed(self):
        self.spell_effect_reset()
        if self.object_2_qty > 0:
            self.ennemy_turn = 1
            self.heal_sound.play()
            self.monster_attack_effect = "images/mana_potion.png"
            self.hero_mp_lost -= 20
            self.object_2_qty -= 1
            self.check_lifes_totals()
            Clock.schedule_once(self.monster_turn, disabled_time)
        else:
            self.fizzle_sound.play()

    def on_object_3_pressed(self):
        self.spell_effect_reset()
        if self.object_3_qty > 0:
            self.ennemy_turn = 1
            self.fire_sound.play()
            self.ids.Effect_spell.source = "images/explosion.png"
            self.monster_hp_lost += 35
            self.object_3_qty -= 1
            self.check_lifes_totals()
            Clock.schedule_once(self.monster_turn, disabled_time)
        else:
            self.fizzle_sound.play()

    def some_method_to_change_object_button_text(self):
        app = kivy.app.App.get_running_app()
        app.root.ids.menu_full.ids.object_menu.ids.object_3.text = 'new'


class AllScreen(Screen):

    def ennemy_killed(self):
        self.ids.Ennemy_img.source = "images/void.png"

    def change_screen(self, screen_to_go):
        self.manager.current = screen_to_go


class MenuFull(TabbedPanel):
    ennemy_turn = BooleanProperty(False)

    def ennemy_turn_on(self):
        self.ennemy_turn = 1
        Clock.schedule_once(self.ennemy_turn_off, 1.5)

    def ennemy_turn_off(self, dt):
        self.ennemy_turn = 0



class PlayerInfo(BoxLayout):
    pass


class MagicMenu(BoxLayout):
    pass


class ObjectMenu(BoxLayout):
    object_1_qty = NumericProperty(3)
    object_2_qty = NumericProperty(3)
    object_3_qty = NumericProperty(3)

    def on_object_3_pressed(self):
        if self.object_3_qty > 0:
            self.object_3_qty -= 1

    def on_object_2_pressed(self):
        if self.object_2_qty > 0:
            self.object_2_qty -= 1

    def on_object_1_pressed(self):
        if self.object_1_qty > 0:
            self.object_1_qty -= 1


class MenuScreen(AllScreen):
    def reset_game_won(self):
        global game_won
        global game_over

        game_won = False
        game_over = False


class GameOverScreen(AllScreen):
    game_over_sound = None

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.init_audio()

    def init_audio(self):
        self.game_over_sound = SoundLoader.load("audio/Game_over.wav")

        self.game_over_sound.volume = 5

    def reset_game_over(self):
        global game_won
        global game_over

        game_won = False
        game_over = False


class FightScreen(AllScreen):

    def check_game_end(self):
        if game_won:
            self.manager.current = 'menu'
        elif game_over:
            self.manager.current = 'game_over'


class TryAgainButton(Button):
    pass


class ActionButton(Button):
    pass


class MagicButton(Button):
    text = "Magie"

    def on_press(self):
        print("Liste des Sorts")


class ObjectButton(Button):
    text = "Objets"

    def on_press(self):
        print("Liste des Objets")


class FleeButton(Button):
    text = "Fuite"


class MonsterFightApp(App):
    pass

    def build(self):
        # Create the screen manager
        sm = ScreenManager(transition=NoTransition())
        sm.add_widget(MenuScreen(name='menu'))
        sm.add_widget(FightScreen(name='combat'))
        sm.add_widget(GameOverScreen(name='game_over'))

        return sm


MonsterFightApp().run()

and here is my kv file :

MenuScreen:


<MenuScreen>:
    on_pre_enter: root.reset_game_won()
    Button:
        text: "FIGHT ! "
        on_press: root.manager.current = 'combat'


<FightScreen>:
    MainWidget:

<GameOverScreen>:
    on_pre_enter: root.game_over_sound.play(), root.reset_game_over()
    on_leave: root.game_over_sound.stop()
    RelativeLayout:
        Label:
            text: "GAME OVER"
            color: 1, 0, 0
            font_size: dp(100)
            font_name: "fonts/Dragon_Fire.otf"
        Button:
            size_hint: 0.2, 0.1
            pos_hint: {'center_x': 0.5, 'center_y': 0.3}
            text: "TRY AGAIN"
            on_release: root.manager.current = 'menu'

<MainWidget>:
    BoxLayout:
        orientation : "vertical"
        BoxLayout:
            size_hint: 1, 0.1
            ProgressBar:
                value: root.hero_xp
        RelativeLayout:
            Image:
                id: Ennemy_img
                source: root.Ennemy_img
            Image:
                id: Effect_spell
                source: root.Effect_img
        ProgressBar:
            size_hint: (root.monster_hp - root.monster_hp_lost) / root.monster_hp, 0.1
            canvas:
                BorderImage:
                    border: (12, 12, 12, 12)
                    pos: self.x, self.center_y - 12
                    size: self.width, 24
                    source: 'images/red.png'
        BoxLayout:
            orientation: "horizontal"
            RelativeLayout:
                MenuFull:
                    id: menu_full
                Button:
                    size_hint: root.ennemy_turn, root.ennemy_turn
                    text: "ENNEMY TURN"
                    size: 0, 0
                    opacity: 0
                    disabled: True
            PlayerInfo:
                orientation : "horizontal"
                RelativeLayout:
                    Image:
                        source : 'images/mage.png'
                    Image:
                        source : root.monster_attack_effect
                BoxLayout:
                    orientation: "vertical"
                    Label :
                        text : "NAME"
                    Label :
                        text : "HP : "
                    ProgressBar:
                        size_hint: (root.hero_hp - root.hero_hp_lost) / root.hero_hp, 0.1
                        canvas:
                            BorderImage:
                                border: (12, 12, 12, 12)
                                pos: self.x, self.center_y - 12
                                size: self.width, 24
                                source: 'images/red.png'
                    Label :
                        text : " MP : "
                    ProgressBar:
                        size_hint: (root.hero_mp - root.hero_mp_lost) / root.hero_mp, 0.1
                        canvas:
                            BorderImage:
                                border: (12, 12, 12, 12)
                                pos: self.x, self.center_y - 12
                                size: self.width, 24
                                source: 'images/blue.png'
                    Label :
                        text : "LVL : " + str(root.hero_lvl)



<MenuFull>:
    do_default_tab: False
    TabbedPanelItem:
        text: "Attaque"
        Button:
            text: "Attack"
            disabled: True if root.ennemy_turn == True else False
            on_press: root.ennemy_turn_on(), self.parent.parent.parent.parent.parent.parent.on_attack(), self.parent.parent.parent.parent.parent.parent.parent.check_game_end()
    TabbedPanelItem
        text: "Magic"
        MagicMenu:
    TabbedPanelItem
        text: "Objets"
        ObjectMenu:
            id: object_menu
    TabbedPanelItem
        text: "Fuite"
        MagicMenu:


<MagicMenu>:
    orientation : "vertical"
    Button:
        text: "Fireball"
        on_press: self.parent.parent.parent.parent.parent.parent.parent.on_spell_fire_pressed(), self.parent.parent.parent.parent.parent.parent.parent.parent.check_game_end()
    Button:
        text: "Healing"
        on_press: self.parent.parent.parent.parent.parent.parent.parent.on_spell_heal_pressed(), self.parent.parent.parent.parent.parent.parent.parent.parent.check_game_end()
    Button:
        text: "tornado"
        on_press: self.parent.parent.parent.parent.parent.parent.parent.on_spell_wind_pressed(), self.parent.parent.parent.parent.parent.parent.parent.parent.check_game_end()


<ObjectMenu>:
    orientation : "vertical"
    BoxLayout:
        Button:
            text: "Healing Potion"
            on_press: self.parent.parent.parent.parent.parent.parent.parent.parent.on_object_1_pressed(), self.parent.parent.parent.parent.parent.parent.parent.parent.parent.check_game_end(), root.on_object_1_pressed()
        Button:
            disabled: True
            disabled_color: 1, 1, 1, 1
            size_hint: 0.2, 1
            text: "x" + str(root.object_1_qty)
    BoxLayout:
        Button:
            text: "Mana Potion"
            on_press: self.parent.parent.parent.parent.parent.parent.parent.parent.on_object_2_pressed(), self.parent.parent.parent.parent.parent.parent.parent.parent.parent.check_game_end(), root.on_object_2_pressed()
        Button:
            disabled: True
            disabled_color: 1, 1, 1, 1
            size_hint: 0.2, 1
            text: "x" + str(root.object_2_qty)
    BoxLayout:
        Button:
            text: "Explosive Potion"
            on_press: self.parent.parent.parent.parent.parent.parent.parent.parent.on_object_3_pressed(), self.parent.parent.parent.parent.parent.parent.parent.parent.parent.check_game_end(), root.on_object_3_pressed()
        Button:
            id : object_3
            disabled: True
            disabled_color: 1, 1, 1, 1
            size_hint: 0.2, 1
            text: "x" + str(root.object_3_qty)

here I want to make ObjectMenu use object_1/2/3_qty From MainWidget but they just won't communicate.

so if anyone has an idea of what to do to achieve that I would love it !



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source