KIVY - Turnier erstellen - List index out of range

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
selbststudium_v2
User
Beiträge: 1
Registriert: Freitag 26. August 2022, 11:29

Hallo zusammen,

ich möchte gerne einen Turnier Generator erstellen.
Hierzu soll zuerst nach allen Namen der Mannschaften gefragt werden und diese anschließend dann auf 4 Gruppen aufgeteilt.

Das aufteilen in die 4 Gruppen hat ohne mein Kivy Framework noch geklappt, wenn ich jetzt jedoch den Code in meine Kivycode einfüge, erhalte ich den Fehler "list index out of range"

Hier mein Code:

Code: Alles auswählen

from kivy.app import App 
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.image import Image
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.clock import Clock
from kivy.uix.popup import Popup
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.properties import ObjectProperty
import random
from klassen import Team
import sqlite3

all_teams = []
anzahl_teams = 5
temp_list = []
zw_dict = {}

Group_A = {}
Group_B = {}
Group_C = {}
Group_D = {}

def listconverttodict():
    pick = random.choice(all_teams[-1])
    zw_dict[pick] = Team(name = pick)
    all_teams.remove(pick)

class First_Screen(Widget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        Clock.schedule_once(self.switch_to_next_view, 0)

    def switch_to_next_view(self, *args):
        app.screen_manager.current ="Second_Screen"

class Second_Screen(Widget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        Group_A = {}

        teaminput = ObjectProperty(None)
        teams_already_added = ObjectProperty(None)

    def button_pressed(self, *args):
        if (self.teaminput.text in all_teams):
            self.popup_double_name(object)
            all_teams.remove(self.teaminput.text)
        if self.teaminput.text == "":
            self.popup_name_forgotten(object)
            if len(all_teams) == 0:
                self.popup_name_forgotten(object)                
        else:
            all_teams.append(self.teaminput.text)

        if len(all_teams) >= anzahl_teams:
            self.ids.move_to_group.disabled = False            
    
        self.teaminput.text = ""
        print(all_teams)

        ausgabe = "Bereits vergebene Teamnamen:"
        for einzel_teams in all_teams:
            ausgabe = f'{ausgabe} \n {einzel_teams}'
            self.ids.teams_already_added.text = f'{ausgabe}'
    
    def popup_name_forgotten(self, object):
        popup = Popup(title='Teamnamen vergessen',
            content=Label(text="Ups! Du hast wohl vergessen einen Teamnamen einzugeben!"),
            size_hint=(None, None), size=(400, 400))
        popup.open()

    def popup_double_name(self, object):
        popup = Popup(title='Sorry, da war wohl jemand schneller!',
            content=Label(text="Der Name ist bereits vergeben!"),
            size_hint=(None, None), size=(400, 400))
        popup.open()

    def teamstogroup(self):
        print(all_teams)
        for single in range(anzahl_teams):
            if len(Group_A) < 4:
                listconverttodict()
                Group_A.update(zw_dict)
                zw_dict.clear()

            if len(Group_B) < 4:
                listconverttodict()
                Group_B.update(zw_dict)
                zw_dict.clear()

            if len(Group_C) < 4:
                listconverttodict()
                Group_C.update(zw_dict)
                zw_dict.clear()

            if len(Group_D) < 4:
                listconverttodict()
                Group_D.update(zw_dict)
                zw_dict.clear()

    def switch_to_third_screen(self, *args):
        app.screen_manager.current = "Third_Screen"


class Third_Screen(Widget):
    pass

class Turnier_Generator(App):
    def build(self):
        
        self.icon = "icon.png"

        self.screen_manager = ScreenManager()
        self.first_screen = First_Screen()
        screen = Screen(name="First_Screen")
        screen.add_widget(self.first_screen)
        self.screen_manager.add_widget(screen)
        
        self.second_screen = Second_Screen()
        screen = Screen(name="Second_Screen")
        screen.add_widget(self.second_screen)
        self.screen_manager.add_widget(screen)

        self.third_screen = Third_Screen()
        screen = Screen(name="Third_Screen")
        screen.add_widget(self.third_screen)
        self.screen_manager.add_widget(screen)

        return self.screen_manager



if __name__ == "__main__":
    app = Turnier_Generator()
    app.run()
meine .kv-Datei:

Code: Alles auswählen

<First_Screen>
    FloatLayout:
        
        canvas.before:
            Rectangle:
                size: root.width, root.height
                pos: self.pos
                source: "background.png"
        
        Label:
            id: welcome_label
            text: "Willkommen zum Turnier Generator!"
            font_size: 32
            size: self.size
            bold: True
            pos: root.width / 2 -40, root.height / 2 + 150

        Label:
            id: go_on_label
            text: "In 5 Sekunden gehts weiter"
            font_size: 14
            color: "black"
            text_size: self.size
            pos: root.center_x - (self.size[0]/2), root.center_y - 200

<Second_Screen>
    teaminput:teaminput

    FloatLayout:

        canvas.before:
            Rectangle:
                size: root.width, root.height
                pos: self.pos
                source: "background.png"

        Label:
            text: "Gib bitte unten die Teamnamen ein"
            font_size: 32
            pos: root.width / 2 - 40,root.height - 200

        Label:
            id: teams_already_added
            text: "Bereits vergebene Teamnamen werden hier angezeigt..."
            font_size: 16
            pos: root.width / 2 - 40, root.height - 400
            size_hint: (0.5,0.1)

        Button:
            id: move_to_group
            text: "Weiter zu den Gruppen"
            disabled: True
            on_press: 
                root.switch_to_third_screen()
                root.teamstogroup()
        
        BoxLayout:
            id: inner_box
            pos: root.width / 6, root.height -500
            size_hint: 6,0.5

            TextInput:
                id: teaminput
                hint_text: "Gib hier die Teamnamen ein"
                multiline: False
                background_color: "black"
                foreground_color: "white"
                cursor_color: "white"

            Button:
                text: "Teamnamen bestätigen"
                size_hint: 0.5,1
                on_press:
                    root.button_pressed()
        

<Third_Screen>
    FloatLayout:

        canvas.before:
            Rectangle:
                size: root.width, root.height
                pos: self.pos
                source: "background.png"


        BoxLayout: 
            orientation: "vertical"
            rows: 3
            size_hint: 3, 1.5
            pos: root.width / 2 - self.size[0]/2, root.height / 2 + self.size[0]/2

            Button:
                text: "Gruppe A"
                color: "black"
                size_hint: 1,0.1

            BoxLayout:
                orientation: "horizontal"
                cols: 3
                size_hint: 1,0.1

                Button:
                    text: "Platz"
                    color: "black"
                    size_hint: 0.25,1

                Button:
                    text: "Name"
                    color: "black"
                    size_hint: 1,1

                Button:
                    text: "Punkte"
                    color: "black"
                    size_hint: 0.25,1

            BoxLayout:
                orientation: "horizontal"
                cols: 3
                size_hint: 1,0.5

                BoxLayout:
                    orientation: "vertical"
                    rows: 4
                    size_hint: .25 , 1

                    Button:
                        text: "1"

                    Button:
                        text: "2"

                    Button:
                        text: "3"

                    Button:
                        text: "4"

                BoxLayout:
                    orientation: "vertical"
                    rows: 4
                    size_hint: 1 , 1

                    Button:
                        id: name_group_a_t1
                        text: "Team1"

                    Button:
                        id: name_group_a_t2
                        text: "Team2"

                    Button:
                        id: name_group_a_t3
                        text: "Team3"

                    Button:
                        id: name_group_a_t4
                        text: "Team4"

                BoxLayout:
                    orientation: "vertical"
                    rows: 4
                    size_hint: .25 , 1

                    Button:
                        id: points_group_a_t1
                        text: "2"

                    Button:
                        id: points_group_a_t2
                        text: "2"

                    Button:
                        id: points_group_a_t3
                        text: "2"

                    Button:
                        id: points_group_a_t4
                        text: "2"
Kann mir hierzu bitte jemand helfen?

Vielen Dank
Viele Grüße
selbststudium
"Probleme kann man niemals mit derselben Denkweise lösen, durch die sie entstanden sind." (Albert Einstein)
Benutzeravatar
Dennis89
User
Beiträge: 1156
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

kannst du bitte den vollständigen Traceback posten?

Um den Code eins zu eins zu übernehmen fehlt zum Beispiel 'klassen'.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
__blackjack__
User
Beiträge: 13112
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@selbststudium_v2: Anmerkungen zum Quelltext: `sqlite3`, `Window`, `BoxLayout`, `Button`, `FloatLayout`, `GridLayout`, `Image`, und `TextInput` werden importiert, aber nirgends benutzt.

`klassen` ist kein sinnvoller Modulname. Man will nicht wissen das da Klassen drin sind, sondern was die Klassen (und Funktionen, und Konstanten), die dort drin sind, bedeuten.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Variablen haben auf Modulebene nichts zu suchen. Der Beitrag klingt so als wenn das vor dem hinzufügen von Kivy als auch schon falsch war. Das könnte man vielleicht erst einmal ohne Kivy korrigieren, denn der saubere Umgang mit Funktionen ist Voraussetzung für den Umgang mit objektorientierter Programmierung (OOP) und das wiederum ist Voraussetzung für GUI-Programmierung.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Man nummeriert keine Namen. Dann will man sich entweder bessere Namen überlegen, oder gar keine Einzelnamen/-werte verwenden, sondern eine Datenstruktur. Oft eine Liste. Wenn man Nummern durch fortlaufende Buchstaben ersetzt, ist das in der Regel auch ”Nummerierung”. `group_a` bis `group_d` sollten nicht als einzelne Namen existieren. Das gleiche gilt für die `FirstScreen`, `SecondScreen` und `ThirdScreen`. Dem Leser nützt ja nicht wirklich zu wissen der wievielte Bildschirm das ist, der will wissen wofür die da sind; was darin angezeigt wird.

Namen sollten keine kryptischen Abkürzungen enthalten oder gar nur daraus bestehen. Der Name soll dem Leser vermitteln was der Wert dahinter im Programm bedeutet, nicht zum rätseln zwingen. Was bedeutet `zw`? Warum gibt es eine Liste mit Temperaturen? Die wird zudem überhaupt nicht verwendet.

Grunddatentypen haben nichts in Namen verloren. Den Typen ändert man gar nicht so selten mal während der Programmentwicklung und dann muss man überall im Programm die betroffenen Namen ändern, oder man hat falsche, irreführende Namen im Quelltext.

Leerzeichenzwischenwortenmachentexteleichterlesbar. Dementsrpechenssolltenuntersctrichebeinamenmitmehrerenwortenverwendetwerden. Zudem hat `listconverttodict()` einen Namen der wieder Grunddatentypen enthält, aber nicht wirklich verrät was die Funktion den eigentlich macht, denn die operiert nicht auf beliebiegen Listen und Wöretbüchern, sondern auf welchen die Werte mit festen Bedeutungen enthalten.

Das ist keine echte Funktion, sondern wird als Name/Sprungmarke für ein Stückchen Code missbraucht, der dann undurchsichtig auf globalen Variablen operiert. Funktion (und Methoden) bekommen alles was sie ausser Konstanten benötigen, als Argument(e) übergeben.

Das globale `zw_dict` wird in der Funktion *einem* Schlüssel/Wert-Paar bestückt, und dann ausserhalb kurz danach wird der Inhalt des Wöretbuchs wieder gelehrt. Letztlich ist das der Rückgabewert dieser Funktion, wenn man es denn tatsächlich wie eine Funktion schreibt.

Die Zeile ``pick = random.choice(all_teams[-1])`` erscheint mir auch falsch, denn da wird aus dem letzten Teamnamen ein Buchstabe ausgewählt, nicht aus allen Teamnamen einer.

Wie kommt eigentlich der Wert für `ANZAHL_TEAMS` zustande? Kann es sein, dass das immer eins mehr als die Anzahl der Gruppen sein muss? Dann sollte man das nicht selbst festlegen, sondern die Anzahl der tatsächlichen Gruppen als Basis dafür nehmen.

`all_teams` scheint mir als Name falsch zu sein, weil es sich nicht um alle Teams/Teamnamen handelt, sondern nur um solche die noch keine Gruppe zugeordnet wurden.

In `TurnierApp.build()` werden Objekte an `self` gebunden die da nicht wirklich hin gehören, sondern einfach nur lokale Namen sein sollten.

Es steht dort dreimal fast der gleiche Code der da nur einmal stehen sollte — in einer Schleife über die Unterschiede.

Die `__init__` von `SecondScreen` macht nichts sinnvolles. Da werden lokale Namen definiert, die dann nirgends verwendet werden.

Die beiden `popup_*()`-Methoden sind keine Methoden. Wenn man das Objekt auf dem eine Methode definiert ist, gar nicht verwendet, dann ist das keine Methode sondern eine Funktion und es stellt sich die Frage was das auf dem Objekt überhaupt zu suchen hat.

Total unsinnig ist dort auch das Argument. Erst mal ist `object` der Name eines eingebauten Datentyps, den sollte man nicht für andere Dinge verwenden, und dann wird das a) überhaupt gar nicht verwendet, und es wird jedes mal der eben erwähnte Datentyp `object` als Argument übergeben, was so überhaupt keinen Sinn ergibt.

Vom Programmablauf ist es ein wenig unsinnig einen bereits in der Liste vorhandenen Namen erst zu löschen, um ihn dann gleich wieder hinzufügen. Wenn der Name schon mal vorkommt, muss man ausser den Benutzer darüber zu informieren, einfach gar nichts mit der Namensliste machen.

`einzel_teams` ist als Name falsch weil das für *einen* Namen steht aber in Mehrzahl benannt ist. Es ist auch sehr umständlich und ineffizient die `ausgabe` auf diese Weise zu erstellen. Dafür gibt es die `join()`-Methode auf Zeichenketten.

In `teamstogroup()` wird `single` definiert aber nicht benutzt. Letztlich ist das dort mit der externen Funktion und den teilsweise sogar nur temporär benutzen globalen Variablen alles viel zu unübersichtlich. Und ineffizient ist es auch. Man würde dort besser die Teamnamen einmal mischen und dann eine Schleife über Paare von Gruppe die noch Teams braucht und Teamname schreiben. Wobei die Bedingung ob eine Gruppe noch Teams braucht etwas komisch ist, wenn jedes mal jeder Gruppe ein Team zugewiesen wird. Denn dann

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import random
from itertools import cycle

from kivy.app import App
from kivy.clock import Clock
from kivy.properties import ObjectProperty
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.widget import Widget
from klassen import Team


class FirstScreen(Widget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        Clock.schedule_once(self.switch_to_next_view, 0)

    def switch_to_next_view(self, *_args):
        App.get_running_app().screen_manager.current = "Second_Screen"


class SecondScreen(Widget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.unassigned_team_names = []
        self.groups = [{} for _ in range(4)]

    def button_pressed(self, *_args):
        team_name = self.teaminput.text

        if team_name in self.unassigned_team_names:
            Popup(
                title="Sorry, da war wohl jemand schneller!",
                content=Label(text="Der Name ist bereits vergeben!"),
                size_hint=(None, None),
                size=(400, 400),
            ).open()

        elif team_name == "":
            Popup(
                title="Teamnamen vergessen",
                content=Label(
                    text="Ups! Du hast wohl vergessen einen Teamnamen einzugeben!"
                ),
                size_hint=(None, None),
                size=(400, 400),
            ).open()

        else:
            self.unassigned_team_names.append(team_name)

        if len(self.unassigned_team_names) >= len(self.groups):
            self.ids.move_to_group.disabled = False

        self.teaminput.text = ""
        print(self.unassigned_team_names)

        self.ids.teams_already_added.text = (
            "Bereits vergebene Teamnamen:\n"
            + "\n".join(self.unassigned_team_names)
        )

    def assign_teams_to_groups(self):
        random.shuffle(self.unassigned_team_names)

        assigned_count = 0
        for team_name, group in zip(
            self.unassigned_team_names, cycle(self.groups)
        ):
            if len(group) < 4:
                group[team_name] = Team(team_name)
                assigned_count += 1

        self.unassigned_team_names = self.unassigned_team_names[
            assigned_count:
        ]

    def switch_to_third_screen(self, *_args):
        App.get_running_app().screen_manager.current = "Third_Screen"


class ThirdScreen(Widget):
    pass


class TurnierGenerator(App):
    def build(self):
        self.icon = "icon.png"

        screen_manager = ScreenManager()

        for name, widget_type in [
            ("First_Screen", FirstScreen),
            ("Second_Screen", SecondScreen),
            ("Third_Screen", ThirdScreen),
        ]:
            screen = Screen(name=name)
            screen.add_widget(widget_type())
            screen_manager.add_widget(screen)

        return screen_manager


def main():
    app = TurnierGenerator()
    app.run()


if __name__ == "__main__":
    main()
Wobei die Gruppen vielleicht nicht zum „zweiten Bildschirm“ gehören. Und das mit den Anzahlen insgesamt ein bisschen komisch gelöst ist.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten