Problem mit ScreenManager

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: 315
Registriert: Samstag 26. Dezember 2015, 16:21

Huhu
Da ich diese Woche noch frei habe, wollte ich mich gern solange mit Python Kivy beschäftigen und dazu lernen.
In meinem Code ist mein Ziel, den ersten Screen "FirstScreen" zu überspringen, wenn ich in einem Windows-System (und nicht im Android-System) bin.
Habe aber mit dem Umschalten über den ScreenManager auf den zweiten Screen "SecondScreen" Probleme.
main.py

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

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


class FirstScreen(Screen):
    def on_enter(self, *args):
        if platform == "android":
            pass
        else:
            print("Windows")
            App.get_running_app().screen_manager.current = 'second_screen'


class SecondScreen(Screen):
    pass


class TestApp(App):
    def __init__(self, **kwargs):
        super(TestApp, self).__init__(**kwargs)
        self.screen_manager = ScreenManager()

    def build(self):
        Builder.load_file("main.kv")
        self.screen_manager.add_widget(FirstScreen(name="first_screen"))
        self.screen_manager.add_widget(SecondScreen(name="second_screen"))
        return self.screen_manager


if __name__ == "__main__":
    TestApp().run()
main.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 = 'left'
                root.manager.current = 'second_screen'


<SecondScreen>:
    BoxLayout:
        orientation: 'vertical'

        Label:
            text: 'This is the Second Screen'

        Button:
            text: 'Go to First Screen'
            on_press:
                root.manager.transition.direction = 'right'
                root.manager.current = 'first_screen'
Der Fehlercode der mir ausgegeben wird, lautet:

Code: Alles auswählen

 kivy.uix.screenmanager.ScreenManagerException: No Screen with name "second_screen".
Windows
Ich habe da wohl was grundlegendes noch nicht verstanden, würde ich sagen :(
Benutzeravatar
Dennis89
User
Beiträge: 1156
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

'on_enter' wird beim ersten Start vor der Logging-Ausgabe 'Start Application mainloop' ausgeführt. Möglich dass da noch nicht alles zur Verfügung steht(?).
Wenn du dass da machen willst, dann erreichst du den ersten Screen ja nie, auch nicht wenn du unter Windows den Button drückst um auf den ersten Bildschirm zurück zu kommen. Ist das so gewollt?
Wenn das Programm einfach beim Start gleich mit dem zweiten Bildschirm anfangen soll, dann würde ich das so machen:

Code: Alles auswählen

#!/usr/bin/env python3

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



class FirstScreen(Screen):
    pass


class SecondScreen(Screen):
    pass


class TestApp(App):
    def build(self):
        Builder.load_file("main.kv")
        screen_manager = ScreenManager()
        screen_manager.add_widget(FirstScreen(name="first_screen"))
        screen_manager.add_widget(SecondScreen(name="second_screen"))
        if platform == "windows":
            screen_manager.current = "second_screen"
        return screen_manager


if __name__ == "__main__":
    TestApp().run()
Dann baust du dir auch nicht unnötiges Zeugs in deine Klasse, das später nur stört, wenn du 'on_enter' vielleicht tatsächlich mal brauchst.
Die 'if'-Abfrage ist so wie du sie hattest etwas unschön, weil du ein Ereignis abfragst und wenn das eintritt machst du nichts. Man fragt normal lieber etwas ab, von dem auch eine Änderung abhängig ist. Dann kann man sich den 'else'-Zweig hin und wieder auch sparen. Also in deinem Fall willst du etwas ändern, wenn du unter Windows bist, dann solltest du genau das auch schreiben. Ist auch verständlicher.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
DMD-OL
User
Beiträge: 315
Registriert: Samstag 26. Dezember 2015, 16:21

Daran hab ich auch gedacht. Aber ich würde es gern so haben, dass ich in den Klassen (und evtl. weitere) auch den ScreenManager ansprechen kann und zudem auch in den jeweiligen Klassen self.ids.
benutzen kann.
Wahrscheinlich brauche ich dann aber eine eigene ScreenManager-Klasse?
DMD-OL
User
Beiträge: 315
Registriert: Samstag 26. Dezember 2015, 16:21

Ich muss das wohl von Grund auf neu machen, schätz ich...

Code: Alles auswählen

 kivy.uix.screenmanager.ScreenManagerException: No Screen with name "second_screen".
py-Datei

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.core.window import Window
from kivy.utils import platform


class FirstScreen(Screen):
    def on_enter(self, *args):
        if platform == "android":  # or platform == "ios"
            Window.maximize()
        else:
            Window.size = (600, 800)  # 600, 1024

        self.ids.lbl_status.text = "Hello World :)"
        TestApp().switch_screen("second_screen")   # <- Muss hier die instance "second_screen" hinein?


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 __init__(self, **kwargs):
        super(TestApp, self).__init__(**kwargs)
        self.screen_manager = ScreenManager()

    def build(self):
        Builder.load_file("main.kv")

        self.screen_manager.add_widget(FirstScreen(name="first_screen"))
        self.screen_manager.add_widget(SecondScreen(name="second_screen"))
        if platform == "windows":
            self.screen_manager.current = "second_screen"
        else:
            print("Android platform")
        return self.screen_manager

    def switch_screen(self, screen):
        self.screen_manager.current = screen


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

Code: Alles auswählen

<FirstScreen>:
    BoxLayout:
        orientation: 'vertical'

        Label:
            id: lbl_status

        Button:
            text: 'Go to Second Screen'
            on_press:
                root.manager.transition.direction = 'left'
                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.transition.direction = 'right'
                root.manager.current = 'first_screen'
Benutzeravatar
Dennis89
User
Beiträge: 1156
Registriert: Freitag 11. Dezember 2020, 15:13

Den Zugriff hattest du ja hier schon

Wobei du nicht den Umweg über 'App.get_running_app' gehen musst. Den aktuellen Screen kannst du auch mit 'self.manager.current' abfragen/ändern.
Das wäre dann folgende Kombination. Wenn du das mit dem abschalten des ersten Bildschirms nicht an der Stelle haben willst, dann hoffe ich das jemand anders noch was in der Doku oder im Quellcode findet. Aber dann wirst du vermutlich wieder erst was sehen, was du nicht sehen willst, bevor es verschwindet. Wobei es auch gut sein kann, das ich mich täusche bzw. etwas übersehe.

Code: Alles auswählen

#!/usr/bin/env python3

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


class FirstScreen(Screen):
    def button_pressed(self):
        self.manager.current = "second_screen"


class SecondScreen(Screen):
    pass


class TestApp(App):
    def build(self):
        Builder.load_file("main.kv")
        screen_manager = ScreenManager()
        screen_manager.add_widget(FirstScreen(name="first_screen"))
        screen_manager.add_widget(SecondScreen(name="second_screen"))
        if platform == "windows":
            screen_manager.current = "second_screen"
        return screen_manager


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

Code: Alles auswählen

<FirstScreen>:
    BoxLayout:
        orientation: 'vertical'

        Label:
            text: 'This is the First Screen'

        Button:
            text: 'Go to Second Screen'
            on_press:
                root.button_pressed()


<SecondScreen>:
    BoxLayout:
        orientation: 'vertical'

        Label:
            text: 'This is the Second Screen'

        Button:
            text: 'Go to First Screen'
            on_press:
                root.manager.transition.direction = 'right'
                root.manager.current = 'first_screen'

Ich finde dass ist gerade das schöne an der kv-Sprache, ich kann mich im Python-Code weitest gehend um die Logik kümmern und das grafische darum erledige ich extra in der *.kv-Datei.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
DMD-OL
User
Beiträge: 315
Registriert: Samstag 26. Dezember 2015, 16:21

Vielleicht kann man ja direkt in die Screen-Managerklasse verweisen, wenn man die auch erstellt. Da sollten dann doch alle Screens wirklich bekannt sein.
Benutzeravatar
Dennis89
User
Beiträge: 1156
Registriert: Freitag 11. Dezember 2020, 15:13

Das machst du schon mit 'self.manager'. 'manager' ist ein ScreenManager-Objekt.
Dokumentiert ist der 'ScreenManager' hier:
https://kivy.org/doc/stable/api-kivy.ui ... en.manager

Die dokumentierten Funktionen kannst du mit deinem 'manager' aufrufen, genau so wie wenn du irgendwo eine Instanz mit 'ScreenManager()' erstellst.

Schau dir die Ausgaben, der zwei 'print'-Funktionen an:

Code: Alles auswählen

#!/usr/bin/env python3

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


class FirstScreen(Screen):
    def button_pressed(self):
        print(self.manager.screen_names)
        self.manager.current = "second_screen"


class SecondScreen(Screen):
    pass


class TestApp(App):
    def build(self):
        Builder.load_file("main.kv")
        screen_manager = ScreenManager()
        screen_manager.add_widget(FirstScreen(name="first_screen"))
        screen_manager.add_widget(SecondScreen(name="second_screen"))
        if platform == "windows":
            screen_manager.current = "second_screen"
        print(screen_manager.screen_names)
        return screen_manager


if __name__ == "__main__":
    TestApp().run()
wenn man die auch erstellt.
Du erstellst die immer in 'build' und danach ist sie verfügbar (siehe 'self.manager'). Ein weiteres mal erstellen bringt nichts, weil du genau auf die die verwendet wird, zugreifen musst, wenn du eine Änderung bewirken willst.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Antworten