Kivy - ScreenManager in ScreenManager - Wie Widgets adressieren

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
Emanuel
User
Beiträge: 7
Registriert: Donnerstag 22. August 2019, 22:35
Wohnort: Herzogenrath
Kontaktdaten:

Hallo zusammen,

mit kivy will ich zur Übung ein kleines Spiel für zwei Spieler im SplitScreen machen. Jeder Spieler kann in seinem Bereich zwischen verschiedenen Screens wechseln. Da beiden Spielern prinzipiell dieselben Screens zur Verfügung stehen und sich nur die angezeigten Daten unterscheiden, habe ich das in der kv-Datei wie nachfolgend definiert. Unterhalb folgen dann die einzelnen Screens jeweils nur einmal.

Auszug aus meiner kv-Datei:

Code: Alles auswählen

<ScreenGameDouble>:
    name: 'screen_game_double'
    BoxLayout:
        orientation: 'horizontal'
        cols: 2

        ScreenManagerLeft:
            id: 'screen_mgr_left'
            transition: FadeTransition()
            ScreenPlayerReady:
            ScreenStartLight:
            ScreenCockpit:
            ScreenRefuel:

        ScreenManagerRight:
            id: 'screen_mgr_right'
            transition: FadeTransition()
            ScreenPlayerReady:
            ScreenStartLight:
            ScreenCockpit:
            ScreenRefuel:
Das funktioniert soweit auch alles.

Nun habe ich in den Screens verschiedene Widgets mit id's bezeichnet. Wie kann ich nun aber auf die jeweiligen Instanzen der ScreenManager zugreifen, um z.B. mit einem Label mit id 'speed' in ScreenCockpit des linken ScreenManagers einen anderen Wert anzuzeigen als mit dem Label derselben id im rechten ScreenManager? Das hier funktioniert jedenfalls nicht:

Code: Alles auswählen

self.ids['screen_mgr_left'].get_screen('screen_cockpit').ids.label_speed.text = str(100)
Wenn ich dagegen nur einen ScreenManager als Haupt-Widget habe, habe ich eine Referenz darauf und kann z.B. das machen:

Code: Alles auswählen

self.scrmgrmain.get_screen('screen_cockpit').ids.label_speed.text = str(100)
Der Unterschied ist also nur, dass ich irgendwie an eine Referenz zu einem untergeordneten ScreenManager kommen muss.

Ich danke vielmals für Tipps und wünsche noch ein schönes restliches Wochenende!
Emanuel
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe wenig Erfahrung mit Kivy. Das die IDs per kv-Dokument eindeutig sein muessen finde ich aber plausibel. Wenn du das umgehen willst, waere die Loesung ggf. deinen Spieler-Teil in eine kv-Datei zu passen, und die zweimal zu laden. Das Ergebnis sollten dann zwei ScreenManage-Objekte sein, mit einem jeweils gleichen ID-Raum. Und die beiden bringst du von Hand nebeneinander zur Anzeige. Das waere zumindest was ich ausprobieren wuerde.
Emanuel
User
Beiträge: 7
Registriert: Donnerstag 22. August 2019, 22:35
Wohnort: Herzogenrath
Kontaktdaten:

__deets__ hat geschrieben: Sonntag 1. September 2019, 10:34 Ich habe wenig Erfahrung mit Kivy. Das die IDs per kv-Dokument eindeutig sein muessen finde ich aber plausibel. Wenn du das umgehen willst, waere die Loesung ggf. deinen Spieler-Teil in eine kv-Datei zu passen, und die zweimal zu laden. Das Ergebnis sollten dann zwei ScreenManage-Objekte sein, mit einem jeweils gleichen ID-Raum. Und die beiden bringst du von Hand nebeneinander zur Anzeige. Das waere zumindest was ich ausprobieren wuerde.
Danke für deinen Beitrag. Genau das ist es doch, was ich im ersten Post beschrieben habe, oder? Ich habe zwar nur eine .kv-Datei verwendet, aber das Ergebnis ist dasselbe: Ich habe zwei ScreenManager, die jeweils denselben ID-Raum enthalten. Die beiden ScreenManager müssen natürlich noch in einem übergeordneten Widget untergebracht werden. Dazu habe ich einen Screen mit einem BoxLayout verwendet. (Nebensache: Einen Screen habe ich deshalb verwendet, weil übergeordnet nochmal ein ScreenManager liegt, der ermöglicht, ein Menü, eine Settings-Dialog oder etwas anderes statt dem Zwei-Spieler-SplitScreen anzuzeigen. Ist hierfür aber nicht relevant.)
Den übergeordneten Screen kann ich adressieren. Nun die Kernfrage: Wie bilde ich je eine Referenz auf die beiden ScreenManager??? Diese Referenzen brauche ich, um dann jeweils manuell im untergeordneten ID-Bereich auf die Widgets zugreifen zu können.

Wie gesagt funktioniert auch das ganze kivy-Layout und die beiden nebeneinanderliegenden ScreenManager können unabhängig voneinander gleiche oder unterschiedliche Screens laden, die ich nur einmal definiert habe.
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nein, EINE Datei zu verwenden ist genau NICHT was ich vorschlage. Der ID-Raum gilt für alles, was in dem Dokument ist. Nur weil du da zwei gleiche Objekte irgendwo hast ändert das nichts daran, dass Objekte UNTER den Managern keine gleiche IDs haben können. Du musst schon zwei KV Dateien benutzen. Die lädst du, und die daraus resultierenden ScreenManager musst du dir halt merken. Doch über die und deren ids Attribut sollte dann ein Zugriff erfolgen können. So zumindest die Hypothese.
Emanuel
User
Beiträge: 7
Registriert: Donnerstag 22. August 2019, 22:35
Wohnort: Herzogenrath
Kontaktdaten:

Ich habe die Lösung für mein Problem gefunden. Mein Ansatz mit einer .kv-Datei ist richtig. Es hat nur nicht geklappt weil… dämlicher Anfängerfehler. In meiner .kv-Datei stand folgendes, siehe auch meinen Ursprungspost:

Code: Alles auswählen

id: 'screen_mgr_left'
Die id darf natürlich keine Zeichenkette sein. So ist es korrekt:

Code: Alles auswählen

id: screen_mgr_left
Und dann gelingt auch der Zugriff, hier aus meinem Hauptwidget:

Code: Alles auswählen

self.scrmgrmain.get_screen('screen_game_double').ids.screen_mgr_left.get_screen('screen_cockpit').ids.label_test.text = str(50)
self.scrmgrmain.get_screen('screen_game_double').ids.screen_mgr_right.get_screen('screen_cockpit').ids.label_test.text = str(100)
Ich habe also nun zwei ScreenManager nebeneinander, die beide mit denselben einmal definierten Screens arbeiten. Der ID-Raum ist also in jedem ScreenManager derselbe (hier z.B. label_test), aber es sind natürlich unterschiedliche Widget-Instanzen.

Und hier noch ein etwas erweiterter Ausschnitt aus meiner .kv-Datei, damit man es besser versteht. Der äußere ScreenManager screen_manager_main ermöglicht ganz regulär die Anzeige verschiedener Screens, z.B. eines Menüs oder der Spieleinstellungen. Außerdem gibt es einen Screen für SinglePlayer und einen für DoublePlayer.
Der SinglePlayer-Screen enthält einen ScreenManager screen_mgr_single, der DoublePlayer-Screen enthält zwei: screen_mgr_left und screen_mgr_right. Diese ScreenManager arbeiten alle mit denselben Screens für die Spielinhalte, die ich nun jeweils nur einmal definieren muss und trotzdem unterscheiden kann, weil ich ja jetzt die inneren ScreenManager adressieren und damit unterscheiden kann.
Ich habe alle Screens weggelassen, die hier nicht von Belang sind.

Code: Alles auswählen

<ScreenManagerMain>:
    id: screen_manager_main
    transition: SlideTransition()
    ScreenIntro:
    ScreenMenu:
    ScreenGameSingle:
    ScreenGameDouble:
    ScreenSettings:

<ScreenGameSingle>:
    name: 'screen_game_single'

    ScreenManagerSingle:
        id: screen_mgr_single
        transition: FadeTransition()
        ScreenPlayerReady:
	ScreenStartLight:
        ScreenCockpit:
        ScreenRefuel:

<ScreenGameDouble>:
    name: 'screen_game_double'
    BoxLayout:
        orientation: 'horizontal'
        cols: 2

        ScreenManagerLeft:
            id: screen_mgr_left
            transition: FadeTransition()
            ScreenPlayerReady:
            ScreenStartLight:
            ScreenCockpit:
            ScreenRefuel:

        ScreenManagerRight:
            id: screen_mgr_right
            transition: FadeTransition()
            ScreenPlayerReady:
            ScreenStartLight:
            ScreenCockpit:
            ScreenRefuel:

<ScreenCockpit>:
    name: 'screen_cockpit'

    Label:
        id: label_test
Danke für alle Hinweise!
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sehr schön. Wie gesagt, das der ID Raum so aufgebaut ist war nur eine Vermutung. Gut zu wissen, dass man das auch so mit verschiedenen scopes machen kann 👍🏻
Antworten