Search code examples
pythonkivywidgetpopup

Add widget dynamically from outside class (error)


I´m learning kivy, and it has passed 3 weeks since i face this problem without encounter a solution, so i hope any of u guys could help me, i would appreciate it.

I have a main file:

from app import MyProgramApp

if __name__ == "__main__":
    winapp = MyProgramApp()
    winapp.run()

from where i start my app. Then i have a directory called "app", inside there is the following "init.py" file.

from kivy.app import App
from kivy.utils import QueryDict, rgba
from kivy.core.window import Window
from .view import MainWindow

Window.minimum_width = 500
Window.minimum_height = 650
Window.maximize()

class MyProgramApp(App):
    colors = QueryDict()
    colors.primary = rgba('#2D9CDB')
    colors.secondary = rgba('#16213E')
    colors.succes = rgba('#1FC98E')
    colors.warning = rgba('#F2C94C')
    colors.danger = rgba('#E85757')
    colors.grey_dark = rgba('#C4C4C4')
    colors.grey_light = rgba('#F5F5F5')
    colors.black = rgba('#A1A1A1')
    colors.white = rgba('#FFFFFF')

    def build(self):
        return MainWindow()

Same folder app, i have the following "view.py".

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.behaviors import ToggleButtonBehavior
from kivy.properties import StringProperty
from kivy.uix.screenmanager import ScreenManager

class MainWindow(BoxLayout):
    username = StringProperty("Usuario")
    def __init__(self, **kw):
        super().__init__(**kw)
    def on_press_home(self):
        self.ids.scrn_mngr.current = "scrn_home"

class ViewManager(ScreenManager):
    def __init__(self, **kw):
        super().__init__(**kw)

class NavTab(ToggleButtonBehavior, BoxLayout):
    text = StringProperty('')
    icon = StringProperty('')
    def __init__(self, **kw):
        super().__init__(**kw)

and finally for that folder i have "myprogram.kv"

#:kivy 2.1.0

#:import Home views.home.Home

<MainWindow>:
    spacing: dp(8)
    canvas.before:
        Color:
            rgba: app.colors.white
        Rectangle:
            pos: self.pos
            size: self.size
    # NAVIGATION BAR
    BoxLayout:
        id: nav_menu
        size_hint_x: .2
        orientation: "vertical"
        size_hint_min_x: dp(100)
        # LOGO 
        BoxLayout:
            id: logo_nbox
            size_hint_y: .1
            size_hint_min_y: dp(70)
            padding: dp(16)
            AnchorLayout:
                anchor_x: "right"
                size_hint_x: None
                width: dp(52)
                halign: "left"
                Label:
                    text: "COMPANY"
                    halign: "center"
            BoxLayout:
                orientation: "vertical"
                Label:
                    text: "COMPANY"
                    halign: "center"
                Label:
                    text: "Phrase"
                    halign: "center"
        # OPTIONS
        GridLayout:
            id: tabs_box
            cols: 1
            spacing: dp(4)
            size_hint_y: .5
            canvas.before:
                Color:
                    rgba: app.colors.grey_dark
                Rectangle:
                    pos: self.pos
                    size: [self.size[0], dp(1)]
            NavTab:
                text: "Home"
                state: "down"
                on_press: root.on_press_home()
            
    # BODY
    BoxLayout:
        size_hint_x: .8
        spacing: dp(8)
        orientation: "vertical"
        padding: [dp(16), dp(8), dp(12), dp(8)]
        canvas.before:
            Color:
                rgba: app.colors.grey_light
            Rectangle:
                pos: self.pos
                size: self.size              
        # SCREENS
        BoxLayout:
            ViewManager:
                id: scrn_mngr
            
<ViewManager>:
    Screen:
        name: "scrn_home"
        Home:
            id: home
                  
                
<NavTab>:
    background_normal: ""
    background_down: ""
    background_color: [0,0,0,0]
    group: "tabs"
    size_hint_y: None
    height: dp(45)
    spacing: dp(4)
    canvas.before:
        Color:
            rgba: [0,0,0,0] if self.state == "normal" else rgba("#E1F1FF")
        Rectangle:
            pos: self.pos
            size: self.size
        Color:
            rgba: [0,0,0,0] if self.state == "normal" else app.colors.primary
        Rectangle:
            pos: [self.pos[0]+self.size[0]-dp(1), self.pos[1]]
            size: [dp(8), self.size[1]]
    Label:
        halign: "left"
        valign: "middle"
        text: root.text
        color: app.colors.grey_dark if root.state == "normal" else app.colors.primary

Then i got another folder called "views" inside i have another folder called "home", inside home we encounter 3 files "init.py", "home.kv", "home.py". the first one "init.py" is the following.

from .home import Home

then we got "home.kv".

#:kivy 2.1.0

<Home>:
    orientation: "vertical"
    Label:
        size_hint_y: .1
        text: "Other"
    Button:
        size_hint_y: .1
        text: "popup"
        on_press: root.open_popup()
    BoxLayout:
        size_hint_y: .8
        id: home_box

<SomePopup>:
    title: "SOME TITLE"
    title_align: "center"
    title_color: app.colors.primary
    size_hint: .25, .8
    size_hint_min_x: dp(200)
    pos_hint: {"x": .1, "top": .9}
    BoxLayout:
        orientation: "vertical"
        padding: [dp(0), dp(12), dp(0), dp(12)]
        Label:
            text: "Some text"
        Button:
            text: "create buttons on box"
            on_press: root.modify_home_box()

and finally and the problem that i´m facing is whit the following file "home.py"

from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.factory import Factory
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.app import App

Builder.load_file('views/home/home.kv')

class Home(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
    
    def open_popup(self):
        Factory.SomePopup().open()

class SomePopup(Popup):
    def __init__(self, **kw):
        super().__init__(**kw)
    
    def modify_home_box(self):
        my_app = App.get_running_app().root
        my_box = my_app.ids.home.ids.home_box
        custom_button = Button(
            text = "something"
        )
        my_box.add_widget(custom_button)

That i´m trying to do is actually modify "home_box" with is store on ids Home dictionary, i also try with ObjectProperty (but that gives and Attribute Error, since i only could read propertys but doesnt modify it), instance of a new Home class doesnt work... searching for current app in App appear doesnt work since Home is store i think on screenManager...

I need add a button or some widget to "home_box" from outside class "SomePopup". I also drop here a repository on github with the whole code. github_repo

I don't know how to solve my issue, and i try with the resources available here on stack as well other net places... any help would be appreciate.


Solution

  • Just a very complicated path to the widget of interest. In your modify_home_box() method, try replacing:

        my_app = App.get_running_app().root
        my_box = my_app.ids.home.ids.home_box
    

    with:

        my_app = App.get_running_app().root
        my_box = my_app.ids.scrn_mngr.ids.home.ids.home_box