Labeltext in Kivy zurückändern

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
DMD-OL
User
Beiträge: 316
Registriert: Samstag 26. Dezember 2015, 16:21

Hallo Burschis!
In meinem App-Beispiel versuche ich den Labeltext mit der id: my_label im zweiten Screen wieder von "Neuer Text" auf 'This is the Second Screen' zurückzuändern.
Wenn man die App startet und dann auf den zweiten Bildschirm wechselt, kann man mit Button "Change label text" den Text des Labels id: my_label von
"This is the Second Screen" auf "Neuer Text!" ändern. Jetzt möchte ich den Text wieder zurück ändern, aber so, dass es im Hintergrund passiert (ohne dass der Benutzer die Änderung wenn auch
nur kurz sehen kann). Darum versuche ich das Label (im SecondScreen) im FirstScreen zurück zu ändern.
Leider funktioniert das aber nicht.

py-Datei

Code: Alles auswählen

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import StringProperty

Builder.load_file("main.kv")


class FirstScreen(Screen):
    label_text = StringProperty()

    def __init__(self, **kwargs):
        super(FirstScreen, self).__init__(**kwargs)
        self.second_screen = SecondScreen()
        self.label_text = self.second_screen.ids.my_label.text

    def on_enter(self, *args):
        self.label_text = "This is the Second Screen"  # Das hier funktioniert nicht :)

    @staticmethod
    def go_to_second():
        app = App.get_running_app()
        app.root.current = "second_screen"


class SecondScreen(Screen):
    def on_enter(self, *args):
        pass

    def change_label_text(self):
        self.ids.my_label.text = "Neuer Text!"

    def go_to_first(self):
        app = App.get_running_app()
        app.root.current = "first_screen"
        # self.ids.my_label.text = "This is the Second Screen"  # Das hier funktioniert, aber man kann das leider kurz sehen :)


class TestApp(App):
    def build(self):
        sm = ScreenManager()

        first_screen = FirstScreen(name="first_screen")
        sm.add_widget(first_screen)

        second_screen = SecondScreen(name="second_screen")
        sm.add_widget(second_screen)

        return sm


if __name__ == "__main__":
    TestApp().run()
kv-Datei

Code: Alles auswählen

<FirstScreen>:
    BoxLayout:
        orientation: 'vertical'
        Label:
            text: 'This is the First Screen'
        Button:
            text: 'Go to Second Screen'
            on_press: root.go_to_second()

<SecondScreen>:
    BoxLayout:
        orientation: 'vertical'
        Label:
            id: my_label
            text: 'This is the Second Screen'
        Button:
            text: 'Change label text'
            on_press: root.change_label_text()
        Button:
            text: 'Go to First Screen'
            on_press: root.go_to_first()
Was mach ich falsch?
Benutzeravatar
Dennis89
User
Beiträge: 1157
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

In 'FirstScreen' funktioniert der Zugriff auf das Label nicht, weil du in der '__init__' eine weitere Instanz von 'SecondScreen' erzeugst. Dass ist aber nicht die Instanz, die du dem ScreenManager zugewiesen hast.
Du müsstest 'FirstScreen' die verwendete Instanz von 'SecondScreen' übergeben.

Eventuell gehts so?

Code: Alles auswählen

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import StringProperty
from time import sleep

Builder.load_file("main.kv")


class FirstScreen(Screen):
    label_text = StringProperty()

    def __init__(self, second_screen, **kwargs):
        super(FirstScreen, self).__init__(**kwargs)
        self.second_screen = second_screen

    def on_enter(self, *args):
        self.second_screen.ids.my_label.text = "This is the Second Screen"

    @staticmethod
    def go_to_second():
        app = App.get_running_app()
        app.root.current = "second_screen"


class SecondScreen(Screen):
    def change_label_text(self):
        self.ids.my_label.text = "Neuer Text!"

    @staticmethod
    def go_to_first():
        app = App.get_running_app()
        app.root.current = "first_screen"


class TestApp(App):
    def build(self):
        sm = ScreenManager()
        second_screen = SecondScreen(name="second_screen")

        first_screen = FirstScreen(second_screen, name="first_screen")
        sm.add_widget(first_screen)
        sm.add_widget(second_screen)

        return sm


if __name__ == "__main__":
    TestApp().run()
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
DMD-OL
User
Beiträge: 316
Registriert: Samstag 26. Dezember 2015, 16:21

Äh, danke für deine Hilfe. ES FUNKTIONIERT :) :)
Aber ich versteh den Fehler nicht so richtig!
Es geht doch ausschließlich um diese Zeile:

Code: Alles auswählen

class FirstScreen(Screen):
    label_text = StringProperty()

    def __init__(self, **kwargs):
        super(FirstScreen, self).__init__(**kwargs)
        self.second_screen = SecondScreen()
        self.label_text = self.second_screen.ids.my_label.text  # <- DIESE HIER
   
Also weil ich mit "self.label_text = self.second_screen.ids.my_label.text" eine zweite Instanz zu self.second_screen erzeuge?
Was genau geschieht denn da, was mir den Zugriff auf self.label_text verweigert?
Ach, ich sehs grad:
Funktioniert das, wenn ich das hier mache :=)

Code: Alles auswählen

 self.label_text.text = self.second_screen.ids.my_label.text
NEIN. Grad getestet.

Code: Alles auswählen

     self.label_text.text = self.second_screen.ids.my_label.text
 AttributeError: 'str' object has no attribute 'text'
????
Benutzeravatar
Dennis89
User
Beiträge: 1157
Registriert: Freitag 11. Dezember 2020, 15:13

Die Zeile über der von dir markieren Zeile erzeugt eine weitere Instanz von 'SecondScreen'.
Dein Programm startet und dann wird das abgearbeitet, dass in 'build' der 'TestApp'-Klasse steht.
Da erzeugst du eine Instanz von 'FirstScreen' und eine von 'SecondScreen'. Diese zwei gibst du an den Screenmanager weiter und alles was angezeigt wird, läuft über diese zwei Instanzen. Wenn du von "irgendwo" auf einen Namen der Klassen zugreifen willst, dann musst du dich auf die Instanz beziehen, die du dem Screenmanager gegeben hast.

Ich finde das aber nicht so schön, dass die eine Klasse von der anderen was wissen muss. Gibt es in 'kivy' nicht auch etwas gegenteiliges von 'on_enter'? Dann könntest du dass da reinschreiben. Wäre auch übersichtlicher.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1157
Registriert: Freitag 11. Dezember 2020, 15:13

So ist schöner:

Code: Alles auswählen

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager

Builder.load_file("main.kv")


class FirstScreen(Screen):
    @staticmethod
    def go_to_second():
        app = App.get_running_app()
        app.root.current = "second_screen"


class SecondScreen(Screen):
    def change_label_text(self):
        self.ids.my_label.text = "Neuer Text!"

    def on_leave(self):
        self.ids.my_label.text = "This is the Second Screen"

    @staticmethod
    def go_to_first():
        app = App.get_running_app()
        app.root.current = "first_screen"


class TestApp(App):
    def build(self):
        screen_manager = ScreenManager()
        screen_manager.add_widget(FirstScreen(name="first_screen"))
        screen_manager.add_widget(SecondScreen(name="second_screen"))

        return screen_manager


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

Grüße
Dennis

Edit:
Die Methoden, die gar nichts von der Klasse brauchen, kannst du dir eigentlich auch sparen und das wechseln der Screens, in der *.kv-Datei vornehmen. Da kannst du dir auch gleich noch eine Wechselrichtung einstellen.

Code: Alles auswählen

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager

Builder.load_file("main.kv")


class FirstScreen(Screen):
    pass


class SecondScreen(Screen):
    def change_label_text(self):
        self.ids.my_label.text = "Neuer Text!"

    def on_leave(self):
        self.ids.my_label.text = "This is the Second Screen"


class TestApp(App):
    def build(self):
        screen_manager = ScreenManager()
        screen_manager.add_widget(FirstScreen(name="first_screen"))
        screen_manager.add_widget(SecondScreen(name="second_screen"))

        return screen_manager


if __name__ == "__main__":
    TestApp().run()
kv

Code: Alles auswählen

<FirstScreen>:
    BoxLayout:
        orientation: 'vertical'
        Label:
            text: 'This is the First Screen'
        Button:
            text: 'Go to Second Screen'
            on_press:
                root.manager.transition.direction = 'down'
                root.manager.current = 'second_screen'

<SecondScreen>:
    BoxLayout:
        orientation: 'vertical'
        Label:
            id: my_label
            text: 'This is the Second Screen'
        Button:
            text: 'Change label text'
            on_press: root.change_label_text()
        Button:
            text: 'Go to First Screen'
            on_press: root.manager.current = 'first_screen'
So ist es laut Doku auch von Kivy vorgesehen.
"When I got the music, I got a place to go" [Rancid, 1993]
DMD-OL
User
Beiträge: 316
Registriert: Samstag 26. Dezember 2015, 16:21

Das kannte ich zwar, bin aber noch nicht richtig im Reinen damit. Vor allem, wenn es darum geht, app oder root oder self. bei Ausdrücken wie
root.change_label_text() oder app.root.change_label_text() oder self.root.change_label_text().
Das wird bei mir eher zu einem Gerate bis es klappt... :(
Antworten