0

How can I access to a label/id of another class using python or kivy and avoiding te error below? I've searched in other posts and I've found it isn't possible to refer to another class by the root one... Hower, I don't know any other method to do that. I hope you could help me. Thanks in advance.

FaceRec.py

Libraries

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager,Screen
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.settings import SettingsWithSidebar
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.actionbar import ActionBar
from kivy.logger import Logger
from kivy.config import Config

Setting the screen size

Config.set('graphics', 'width', '850')
Config.set('graphics', 'height', '850')

HomeScreen classes

class ScreenManagement(ScreenManager):
    pass

class HomeScreen(Screen):
    pass

class HomeActionBar(ActionBar):
    pass

class TitleLabel(Label):
    pass

class StatusBoxLayout(BoxLayout):
    pass

class ErrorsBoxLayout(BoxLayout):
    pass

Main class

class FaceRecApp(App):
    def build(self):
        root = HomeScreen()
        Logger.info('FaceRec.py: FaceRec.kv loaded')
        self.settings_cls = MySettingsWithSidebar
        Logger.info('FaceRec.py: MySettingsWithSidebar loaded')

        status = root.ids.StatusBoxLayout.status
        status.font_size = float(self.config.get('General', 'font_size'))
        label = root.ids.label
        label.font_size = float(self.config.get('General', 'font_size'))

        return root

    def build_config(self, config):
        config.setdefaults('General', {'text': 'Default Hello','font_size': 20})

    def build_settings(self, settings):
        settings.add_json_panel('General', self.config, 'Settings.json')

    def on_config_change(self, config, section, key, value):
        Logger.info("FaceRec.py: App.on_config_change: {0}, {1}, {2}, {3}".format(config, section, key, value))

        if section == "General":
            if key == "text":
                self.root.ids.label.text = value
            elif key == 'font_size':
                self.root.ids.label.font_size = float(value)
                self.root.ids.StatusBoxLayout.status = float(value)

    def close_settings(self, settings):
        Logger.info("FaceRec.py: App.close_settings: {0}".format(settings))
        super(FaceRecApp, self).close_settings(settings)

Sidebar settings

class MySettingsWithSidebar(SettingsWithSidebar):
    def on_close(self):
        Logger.info('FaceRec.py: MySettingsWithSidebar.on_close')

    def on_config_change(self, config, section, key, value):
        Logger.info(
            "FaceRec.py: MySettingsWithSidebar.on_config_change: "
        "{0}, {1}, {2}, {3}".format(config, section, key, value))

Execute

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

FaceRec.kv

ScreenManagement:
HomeScreen:

<HomeActionBar>:
    id: HomeActionBar
    pos_hint: {'bottom':0}
    ActionView:
        use_separator: True
        ActionPrevious:
            title: 'Home'
            with_previous: False
        ActionOverflow:
        ActionButton:
            text: 'Settings'
            icon: 'settings.png'
            background_down: 'settings.png'
            on_release:
                app.open_settings()

<TitleLabel>:
    id: TitleLabel
    text: '[b]FaceRec[/b] - [i]The Face Recognition Project[/i]'
    color: 0.0,0.3,1,1
    markup: True
    font_size: 38

<StatusBoxLayout>:
    orientation: 'horizontal'
    #padding: 100
    Label:
        id: status
        text: 'Status: '
        font_size: 20
    Label:
        id: status_value
        text: 'Error'
        font_size: 20
        color: 1,0,0,1

<ErrorsBoxLayout>:
    id: ErrorsBoxLayout
    orientation: 'horizontal'
    #padding: 100
    Label:
        id: errors
        text: 'Errors No: '
        font_size: 20
    Label:
        id: errors_value
        text: '...'
        font_size: 20

<HomeScreen>:
    id: HomeScreen
    BoxLayout:
        orientation: 'vertical'
        HomeActionBar:
        TitleLabel:

        BoxLayout:
            cols: 2
            orientation: 'vertical'

            StatusBoxLayout:
            ErrorsBoxLayout:

    Label:
        id: label
        text: 'Does it run?'

Settings.json

[
    {
        "type": "string",
        "title": "Label caption",
        "desc": "Choose the text that appears in the label",
        "section": "General",
        "key": "text"
    },
    {
        "type": "numeric",
        "title": "Label font size",
        "desc": "Choose the font size the label",
        "section": "General",
        "key": "font_size"
    }
]

I've tried to access to label variable (FaceRecApp, build and on_config_change functions) and the program has run properly. When I've changed the font size of the label in settings, changes has been applied in the HomeScreen. Then I've added the following lines of code:

In build():

status = root.ids.StatusBoxLayout.status
status.font_size = float(self.config.get('General', 'font_size'))

and in on_config_change():

self.root.ids.StatusBoxLayout.status = float(value)

The result has been an error:

[INFO              ] [Logger      ] Record log in C:\***\.kivy\logs\kivy_17-04-17_109.txt
[INFO              ] [Kivy        ] v1.9.1
[INFO              ] [Python      ] v2.7.13 (v2.7.13:a06454b1afa1, Dec 17 2016, 20:53:40) [MSC v.1500 64 bit (AMD64)]
[INFO              ] [Factory     ] 179 symbols loaded
[INFO              ] [Image       ] Providers: img_tex, img_dds, img_gif, img_sdl2, img_pil (img_ffpyplayer ignored)
[INFO              ] [Text        ] Provider: sdl2
[INFO              ] [OSC         ] using <thread> for socket
[INFO              ] [Window      ] Provider: sdl2
[INFO              ] [GL          ] GLEW initialization succeeded
[INFO              ] [GL          ] OpenGL version <4.5.0 NVIDIA 376.53>
[INFO              ] [GL          ] OpenGL vendor <NVIDIA Corporation>
[INFO              ] [GL          ] OpenGL renderer <GeForce ***/PCIe/SSE2>
[INFO              ] [GL          ] OpenGL parsed version: 4, 5
[INFO              ] [GL          ] Shading version <4.50 NVIDIA>
[INFO              ] [GL          ] Texture max size <16384>
[INFO              ] [GL          ] Texture max units <32>
[INFO              ] [Window      ] auto add sdl2 input provider
[INFO              ] [Window      ] virtual keyboard not allowed, single mode, not docked
[INFO              ] [FaceRec.py  ] FaceRec.kv loaded
[INFO              ] [FaceRec.py  ] MySettingsWithSidebar loaded
 Traceback (most recent call last):
   File "C:/***/PycharmProjects/face_recognition_2/FaceRec.py", line 114, in <module>
     FaceRecApp().run()
   File "C:\Python27\lib\site-packages\kivy\app.py", line 802, in run
     root = self.build()
   File "C:/***/PycharmProjects/face_recognition_2/FaceRec.py", line 65, in build
     status = root.ids.StatusBoxLayout.status
   File "kivy\properties.pyx", line 757, in     kivy.properties.ObservableDict.__getattr__ (kivy\properties.c:11882)
 AttributeError: 'super' object has no attribute '__getattr__'

Process finished with exit code 1

I've already read this post (and many others...): How to access id/widget of different class from a kivy file (.kv)? but my program hasn't run yet.

Again, thanks for your support.

Community
  • 1
  • 1
A.B.
  • 5
  • 4

1 Answers1

0

You forgot to give StatusBoxLayout an id in your root class (HomeScreen). The id needs to be given in the root class, to be able to accsess it as self.root.ids.StatusBoxLayout.
So give it its id in HomeScreen:

<HomeScreen>:
    id: HomeScreen
    BoxLayout:
        orientation: 'vertical'
        HomeActionBar:
        TitleLabel:

        BoxLayout:
            cols: 2
            orientation: 'vertical'

            StatusBoxLayout:
                id: StatusBoxLayout
            ErrorsBoxLayout:
                id: ErrorsBoxLayout

And while you are at it, you might as well give an id to ErrorsBoxLayout too, or any other class you need to access by id.

Now when you assign status = root.ids.StatusBoxLayout.status, assign it like this instead:

status = self.root.ids.StatusBoxLayout.ids.status

You need to prepend self to that because later in your App class, you want to get or set self.root. But you did not define root as an class attribute.
And the second ids is because status is an id of StatusBoxLayout
So your build method, should look like this:

def build(self):
    self.root = HomeScreen()
    Logger.info('FaceRec.py: FaceRec.kv loaded')
    self.settings_cls = MySettingsWithSidebar
    Logger.info('FaceRec.py: MySettingsWithSidebar loaded')

    status = self.root.ids.StatusBoxLayout.ids.status
    status.font_size = float(self.config.get('General', 'font_size'))
    label = self.root.ids.label
    label.font_size = float(self.config.get('General', 'font_size'))

    return self.root
el3ien
  • 5,362
  • 1
  • 17
  • 33