Kivy: Access configuration values from any widget

2020-07-10 09:58发布

问题:

I'm using kivy to create a small App for computer aided learning.

At the moment I have some problems with accessing config values. I get the value with

self.language = self.config.get('basicsettings', 'language')

in the main App-Class. That works fine, however, I don't know how to access these value inside another widget - in this case the AudioButton.

I'm using a ScreenManager, which contains a Screen. Inside is a BoxLayout, which contains a GridLayout and this holds several AudioButtons.

Now, in this AudioButton I want to know the current value of self.language defined in the mainApp.

In .kv files I can do something like

`text: app.language`

to get it, but how to do it directly in Python?

If I use a dummy label in kv to get the value, it works, but when I change the setting, I need to restart the App, because I don't know what I need to add to on_config_change() to update the value during runtime.

Here's a very simplified version of my App with all interesting parts, I hope.

class AudioButton(Button):
    filename = StringProperty(None)
    sound = ObjectProperty(None, allownone=True)

    def on_press(self):
        if self.ids.playsound.text == '1':
            self.sound.play()
        else:
            print('NoSound')


class MainScreen(Screen):
    pass


class Pictures1(GridLayout):
    def __init__(self, **kwargs):
        super(Pictures1, self).__init__(**kwargs)
        self.cols = 2
        btn = AudioButton()
        self.add_widget(btn)
        btn = AudioButton()
        self.add_widget(btn)


class Lesson1(Screen):
    pass


class ScreenManagement(ScreenManager):
    pass


class LunahutsoApp(App):
    def build(self):
        self.settings_cls = SettingsWithSidebar
        self.use_kivy_settings = False
        self.language = self.config.get('basicsettings', 'language')
        self.playsound = self.config.get('basicsettings', 'playsound')
        return ScreenManagement()

    def build_config(self, config):
        config.setdefaults('basicsettings', {
            'language': 'austrian',
            'playsound': 1})

    def build_settings(self, settings):
        settings.add_json_panel('Lunahutso',
                                self.config,
                                data=settings_json)

    def on_config_change(self, config, section,
                         key, value):
        if key == 'language':
            self.language = value
        if key == 'playsound':
            self.playsound = value


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

And the .kv file:

<ScreenManagement>:
    MainScreen:
    Lesson1:

<AudioButton>:
    Label:
        id: language
        text: app.language
        color: 0, 0, 0, 0
    Label:
        id: playsound
        text: app.playsound
        color: 0, 0, 0, 0

<MainScreen>:
    name: "main"
    BoxLayout:
        orientation: 'vertical'
        Button:
            on_release: app.root.current = "lesson1"
            text: "Lesson"
            font_size: 50
        Button:
            on_release: app.open_settings()
            text: "Settings"
            font_size: 50
        Button:
            on_release: sys.exit()
            text: "Quit"
            font_size: 50

<Lesson1>:
    name: "lesson1"
    id: lesson1
    BoxLayout:
        orientation: 'vertical'
        Pictures1:
            size_hint_y: 0.5
        BoxLayout:
            size_hint_y: 0.15
            Label:
                text: ""

回答1:

You can use the following method of the app class get_running_app(), see here https://kivy.org/docs/api-kivy.app.html#kivy.app.App.get_running_app

This way you can reference the config from another class through the app class.

I wrote a quick example below. I am using self.text = App.get_running_app().config.get('Label','content') to access sth in the config.

from kivy.app import App
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.config import Config


class Labelwithconfig(Label):

    def check_label(self):
        self.text = App.get_running_app().config.get('Label','content')

kv_str = Builder.load_string("""
BoxLayout:
    orientation: 'vertical'
    Labelwithconfig:
        id: labelconf
    Button:
        text: 'open settings'
        on_press: app.open_settings()
""")



class MyApp(App):
    def build_config(self, config):
        config.setdefaults('Label', {'Content': "Default label text"})

    def build_settings(self, settings):
        settings.add_json_panel("StackOverflow Test Settings", self.config, data="""
        [
        {"type": "options",
        "title": "Label text System",
        "section": "Label",
        "key": "Content",
        "options": ["Default label text", "Other Label text"]
        }
        ]"""
        )
    def on_config_change(self, config, section, key, value):
        self.root.ids.labelconf.check_label()

    def build(self):
        return kv_str


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


回答2:

An alternative to App.get_running_app().config is to use ConfigParser.get_configparser('app').

This gets you your config object directly.

An even better method, if you want to bind to the values or do more than just look them up once, is to use ConfigParserProperty.

This let's you use values from your config directly as properties without having to deal with the config yourself or monitor config changes:

class LunahutsoApp(App):
    language = ConfigParserProperty('austrian', 
                    'basicsettings', 'language', 'app')
    playsound = ConfigParserProperty(1, 
                    'basicsettings', 'playsound', 'app')

    ...

Then you can bind the values in Kv (NOTE: the way you defined langauge and playsound, your Kv labels will not auto update, as they are not Properties):

<AudioButton>:
    Label:
        id: language
        text: app.language
        color: 0, 0, 0, 0
    Label:
        id: playsound
        text: app.playsound
        color: 0, 0, 0, 0

You can also define a ConfigParserProperty on any Screen or Widget (not just the App):

class AudioButton(Button):
    filename = StringProperty(None)
    sound = ObjectProperty(None, allownone=True)
    playsound = ConfigParserProperty('1', 
                    'basicsettings', 'playsound', 'app')


<AudioButton>:
    Label:
        id: language
        text: app.language
        color: 0, 0, 0, 0
    Label:
        text: self.playsound
        color: 0, 0, 0, 0
        on_press: if self.playsound == '1': self.sound.play()


标签: python kivy