Höhe von Widgets anpassen

Fragen zu Tkinter.
Antworten
Caskuda
User
Beiträge: 26
Registriert: Sonntag 15. März 2020, 22:09

Servus,

ich Bastel gerade an einer Punktetabelle fürs Kniffeln.


Ich hatte zunächst kleinteilig angefangen und eine Liste der einzelnen Namen und die Eingabefelder pro Spieler separat
in Frames angelegt.
Nun habe ich das Problem, dass die Entry-Felder und die Label unterschiedliche Höhe haben, und ich eine zeilenweise Ausrichtung verliere.

Gibt es eine Möglichkeit eine feste Höhe in Pixeln für die einzelnen Widgets zu definieren?
height scheint es leider nicht zu sein.



Minimalbeispiel:

Code: Alles auswählen

import tkinter as tk

class Kniffel(tk.Tk):
    def __init__(self):
        super().__init__()

        self.beschreibung = tk.Frame()
        self.beschreibung.pack(side = tk.LEFT)

        self.eingabefelder = tk.Frame()
        self.eingabefelder.pack(side = tk.RIGHT)


        self.labels = [tk.Label(self.beschreibung, text = "Platzhalter") for _ in range(15)]
        [label.pack() for label in self.labels]

        self.entries = [tk.Entry(self.eingabefelder) for _ in range(15)]
        [entry.pack() for entry in self.entries]


if __name__ == '__main__':
    app = Kniffel()
    app.mainloop()

Optisch wollte ich hierhin:

Code: Alles auswählen

import tkinter as tk

def main():
    app = App()
    app.mainloop()


class App(tk.Tk):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        for i in range(15):
            beschreibung = tk.Label(self, text = "Beschreibung: ")
            eingabe = tk.Entry(self)
            beschreibung.grid(row = i, column = 0)
            eingabe.grid(row = i, column = 1)


if __name__ == '__main__':
    main()
Jedoch war meine Zielsetzung, bei Auswertung, Hinzufügen oder Entfernen von Spielern, nicht dauernd über das Grid des Hauptfensters zu iterieren.


Kompletter Code:

Code: Alles auswählen

import tkinter as tk

def main():
    game = Kniffel()
    game.mainloop()


class Kniffel(tk.Tk):
    def __init__(self, players = None):
        players = ["Hans", "Ute", "Paul"]
        super().__init__()
        self.title("Kniffel")

        self.description = Description()
        self.description.grid(column = 1, row = 0, sticky = "ne")

        self.players = [Player(name) for name in players]
        for i, player in enumerate(self.players):
            player.grid(column = i + 1 + 1, row = 0) # +1 Steuerung, +1 Beschreibung


        # -- Control Buttons --
        self.control = tk.Frame()
        self.control.grid(column = 0, row = 0, sticky = "nw")

        self.get_points = tk.Button(self.control, text = "Ergebnis", command = self.sum_up)
        self.get_points.pack()

        self.reset = tk.Button(self.control, text = "zurücksetzen", command = self.reset_points)
        self.reset.pack()

        self.delete = tk.Button(self.control, text = "lösche Spieler", command = self.remove_player)
        self.delete.pack()

        self.new_player = tk.Entry(self.control, width = len("neuer Spieler"))
        self.new_player.pack()
        self.new_player.insert(tk.END, 'neuer Spieler')

        self.add_p = tk.Button(self.control, text="hinzufügen", command = self.add_player)
        self.add_p.pack()


    def sum_up(self):
        [player.sum_up() for player in self.players]


    def reset_points(self):
        [player.reset_points() for player in self.players]


    def remove_player(self):
        if self.players:
            self.players[-1].destroy()
            del self.players[-1]


    def add_player(self):
        if self.new_player.get() != "neuer Spieler":
            self.players.append(Player(self.new_player.get()))
            self.players[-1].grid(column = len(self.players) + 1, row = 0)
            self.new_player.delete(0, tk.END)
            self.new_player.insert(0, "neuer Spieler")


class Player(tk.Frame):
    def __init__(self, name):
        super().__init__()
        self.name = tk.Label(self, text = name)
        self.eyes = [tk.Entry(self, width = max(len(name),5)) for _ in range(6)]

        self.bonus = tk.Label(self, text = " - ")
        self.sum = tk.Label(self, text = " - ")

        self.specials = [tk.Entry(self, width = max(len(name),5)) for _ in range(7)]

        self.sum2 = tk.Label(self, text = " - ")
        self.total = tk.Label(self, text = " - ")

        # -- Layout --
        self.name.pack()
        [x.pack() for x in self.eyes]

        self.bonus.pack()
        self.sum.pack()

        [x.pack() for x in self.specials]

        self.sum2.pack()
        self.total.pack()


    def sum_up(self):
        summe = 0
        try:
            summe += int(self.eyes[0].get())
        except:
            pass

        for eye in self.eyes:
            try:
                summe += int(eye.get())
            except:
                pass

        if summe >= 63:
            self.bonus.config(text = "25")
            summe += 25
        else:
            self.bonus.config(text = "0")
        self.sum.config(text = str(summe))#text(summe)

        summe2 = 0
        for special in self.specials:
            try:
                summe2 += int(special.get())
            except:
                pass

        self.sum2.config(text = summe2)
        self.total.config(text = summe + summe2)


    def reset_points(self):
        self.sum.config(text = " - ")
        self.bonus.config(text = " - ")
        self.sum2.config(text = " - ")
        self.total.config(text = " - ")

        [x.delete(0, tk.END) for x in self.eyes]
        #[x.insert(0, "") for x in self.eyes]
        [x.delete(0, tk.END) for x in self.specials]
        #[x.insert(0, "") for x in self.specials]


class Description(tk.Frame):
    def __init__(self):
        super().__init__()

        names = ["Legende", "1: ", "2: ", "3: ", "4: ", "5: ", "6:",
                "Bonus:", "Summe:","Full House: ", "kleine Straße: ",
                "große Straße: ", "dreier Pasch: ", "vierer Pasch: ", "Chance: ", 
                "Kniffel: ", "Summe", "Total"]
        self.names = [tk.Label(self, text = name) for name in names]

        # -- Layout --
        [x.pack() for x in self.names]


if __name__ == '__main__':
    main()

Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Listcomprehensions sind nicht ein Ersatz für for-Schleifen, wenn Du damit eine Liste produzierst, die Du gar nicht brauchst. Benutze keine nakten excepts, weil die auch viele Programmierfehler abfangen.

grid ist die Lösung, wie Du schon selbst festgestellt hast.
Caskuda
User
Beiträge: 26
Registriert: Sonntag 15. März 2020, 22:09

Sirius, vielen lieben Dank für die Hinweise.

Ich habe nun auf .grid() umgestellt.
Die nackten excepts habe ich erstmal rausgenommen und prüfe momentan, ob der String nur aus den Zeichen 0-9 besteht.
- hier sollte ich mich auch dransetzen auf ausschließlich zulässige Würfe zu prüfen.

mögliche ToDo's:
- Bei den Straßen, Full House und Kniffel bieten sich ggf. noch Radio-Buttons oder DropDown-Menüs an.
- ansprechen der Spielerwürfe über ein Dictionary


Bei weiteren Schnitzern, schlechtem Stil oder Verbesserungsvorschlägen gerne her damit.



Code: Alles auswählen

import tkinter as tk
import re

def main():
    game = Kniffel()
    game.mainloop()


class Kniffel(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Kniffel")
        light = "#468DAE"
        dark = "#0F668E"
        self.geometry("500x680")
        self.kniffel = "Kniffel"
        self.player_management = PlayerManagement(master = self)
        self.game_board = GameBoard(master = self)
        self.game_flow = GameFlow(master = self)

        # -- Layout --
        self.player_management.grid(column = 0, row = 0, sticky = "nw")
        self.game_board.grid(column = 0, row = 1, sticky = "nw")
        self.game_flow.grid(column = 0, row = 2, sticky = "nw")


    def remove_player(self):
        self.game_board.remove_player()

    def add_player(self, name):
        self.game_board.add_player(name)

    def sum_up(self):
        self.game_board.sum_up()

    def reset_points(self):
        self.game_board.reset_points()


class PlayerManagement(tk.Frame):
    def __init__(self, master):
        light = "#468DAE"
        dark = "#0F668E"
        super().__init__(bg = dark)
        self.config(bg = dark)
        self.master = master

        # -- Control Buttons --
        self.new_player = tk.Entry(self, width = len("neuer Spieler"), relief = tk.RIDGE)
        self.add_p = tk.Button(self, text = "hinzufügen", relief = tk.RIDGE, command = self.add_player)
        self.delete = tk.Button(self, text = "lösche Spieler", relief = tk.RIDGE, command = self.remove_player)

        # -- Layout --
        self.new_player.grid(row = 0, column = 0, columnspan = 2, padx = 5, pady = 5)
        self.new_player.insert(tk.END, 'neuer Spieler')
        self.add_p.grid(row = 0, column = 3, padx = 5, pady = 5)
        self.delete.grid(row = 0, column = 4, padx = 5, pady = 5)


    def add_player(self):
        if self.new_player.get() == "neuer Spieler":
            self.new_player.config(fg = "RED")
        else:
            self.master.add_player(name = self.new_player.get())
            self.new_player.config(fg = "black")

            self.new_player.delete(0, tk.END)
            self.new_player.config(fg = "black")
            self.new_player.insert(0, "neuer Spieler")

    def remove_player(self):
        self.master.remove_player()


class GameFlow(tk.Frame):
    def __init__(self, master):
        light = "#468DAE"
        dark = "#0F668E"
        super().__init__()
        self.master = master
        self.config(bg = dark)

        # -- Control Buttons --
        self.get_points = tk.Button(self, text = "Ergebnis", relief = tk.RIDGE, command = self.sum_up)
        self.reset = tk.Button(self, text = "zurücksetzen", relief = tk.RIDGE, command = self.reset_points)

        # -- Layout --
        self.get_points.grid(row = 0, column = 0, columnspan = 1, padx = 5, pady = 5)
        self.reset.grid(row = 0, column = 1, columnspan = 1, padx = 5, pady = 5)


    def sum_up(self):
        self.master.sum_up()

    def reset_points(self):
        self.master.reset_points()


class GameBoard(tk.Frame):
    def __init__(self, master):
        self.light = "#468DAE"
        self.dark = "#0F668E"
        self.dark2 = "#093E62"
        super().__init__(master)
        self.master = master
        self.config(bg = self.dark)
        self.num_players = 0
        self.players = list()

        names = ["Legende", "1: ", "2: ", "3: ", "4: ", "5: ", "6:",
                "Bonus:", "Summe:","Full House: ", "kleine Straße:",
                "große Straße:", "dreier Pasch:", "vierer Pasch:", "Chance:",
                "Kniffel:", "Summe:", "Total:"]
        self.rows = len(names)

        for i, name in enumerate(names):
            description = tk.Label(self, text = name, width = 11,bg = self.dark2, fg = "white", relief = tk.GROOVE)
            description.grid(column = 0, row = i, sticky = "e", padx = 15, pady = 5)

        self.entries = list()

    def add_player(self, name):
        self.num_players += 1

        self.players.append(list())
        self.players[-1].append(tk.Label(self, text = name, fg = "white", bg = self.dark2))
        # Augen
        for _ in range(6):
            self.players[-1].append(tk.Entry(self, width = max(8, len(name)), bg = self.dark2, fg = "white"))
            self.players[-1][-1].insert(0, '0')
        # Summe, Bonus
        self.players[-1].append(tk.Label(self, text = " - ", fg = "white", width = max(8, len(name)), bg = self.dark2, relief = tk.GROOVE))
        self.players[-1].append(tk.Label(self, text = " - ", fg = "white", width = max(8, len(name)), bg = self.dark2, relief = tk.GROOVE))
        # spezielle Würfe
        for _ in range(7):
            self.players[-1].append(tk.Entry(self, width = max(8, len(name)), bg = self.dark2, fg = "white"))
            self.players[-1][-1].insert(0, '0')
        # Summe, Total
        self.players[-1].append(tk.Label(self, text = " - ", fg = "white", width = max(8, len(name)), bg = self.dark2, relief = tk.GROOVE))
        self.players[-1].append(tk.Label(self, text = " - ", fg = "white", width = max(8, len(name)), bg = self.dark2, relief = tk.GROOVE))

        # -- Layout --
        for i, widget in enumerate(self.players[-1]):
            widget.grid(column = self.num_players, row = i)


    def remove_player(self):
        if self.num_players > 0:
            self.num_players -=1
            for widget in self.players[-1]:
                widget.destroy()
                del widget
            self.players.pop()


    def sum_up(self):
        pattern = re.compile("\d*")
        flag = True

        for player_scores in self.players:
            summe1 = 0
            for i in range(1, 7):
                if pattern.fullmatch(player_scores[i].get()) is not None:
                    summe1 += int(player_scores[i].get())
                    player_scores[i].config(bg = self.dark2)
                else:
                    print(pattern.fullmatch(player_scores[i].get()))
                    player_scores[i].config(bg = "RED")
            bonus = 0 if summe1 < 63 else 25

            summe2 = 0
            for i in range(9, 16):
                summe2 += int(player_scores[i].get())

            player_scores[7].config(text = summe1)
            player_scores[8].config(text = bonus)
            player_scores[-2].config(text = summe2)
            player_scores[-1].config(text = summe1 + summe2 + bonus)


    def reset_points(self):
        for player_scores in self.players:
            for i in range(1, 7):
                player_scores[i].delete(0, tk.END)
                player_scores[i].insert(0, "0")

            for i in range(9, 16):
                player_scores[i].delete(0, tk.END)
                player_scores[i].insert(0, "0")

            player_scores[7].config(text = "-")
            player_scores[8].config(text = "-")
            player_scores[-2].config(text = "-")
            player_scores[-1].config(text = "-")


if __name__ == '__main__':
    main()
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Um die Gleichheitszeichen bei Key-Word-Argumenten kommen keine Leerzeichen.
Zeile 13,14: light und dark sollten Kontanten sein, in GameBoard sind das sogar Attribute.
Zeile 16: Kniffel.kniffel wird gar nicht benutzt.
Zeile 27ff: die Methoden reichen den Aufruf nur an game_board weiter. Das heißt eigentlich, dass PlayerManagement und GameFlow eine Abhängigkeit zu game_board haben, die man explizit machen könnte.
Zeile 51f: nicht alles muß an Attribute gebunden werden. Benutze keine Abkürzungen.
Bei PlayerManagement und GameFlow stellt sich fast die Frage, ob das überhaupt eigene Klassen sein müssen.
Zeile 114: rows wird nicht benutzt. Ebensowenig entries
Zeile 123: num_players ist nur die Länge der Liste players, also überflüssig.
Zeile 126: da braucht es eine Lokale Variable player.
Zeile 152: del hat hier absolut keine Wirkung, wie meistens bei Variablen.
Zeile 157: zum Zahlentesten ist ein regulärer Ausdruck ein bißchen viel.
Zeile 175ff: zu viele magische Nummern.

Zwischenstand:

Code: Alles auswählen

import tkinter as tk

LIGHT = "#468DAE"
DARK = "#0F668E"
DARK2 = "#093E62"

def main():
    game = Kniffel()
    game.mainloop()


class Kniffel(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Kniffel")
        self.config(bg=DARK)
        self.game_board = GameBoard(self)
        self.player_management = PlayerManagement(self, self.game_board)

        # -- Layout --
        self.player_management.grid(column=0, row=0, sticky="nw")
        self.game_board.grid(column=0, row=1, sticky="nw")

        game_flow = tk.Frame(self, bg=DARK)
        tk.Button(game_flow,
            text="Ergebnis", relief=tk.RIDGE, command=self.game_board.sum_up
        ).grid(row=0, column=0, columnspan=1, padx=5, pady=5)
        tk.Button(game_flow,
            text="zurücksetzen", relief=tk.RIDGE, command=self.game_board.reset_points
        ).grid(row=0, column=1, columnspan=1, padx=5, pady=5)
        game_flow.grid(column=0, row=2, sticky="nw")


class PlayerManagement(tk.Frame):
    def __init__(self, master, game_board):
        super().__init__(master, bg=DARK)
        self.game_board = game_board
        self.new_player = tk.Entry(self, width=len("neuer Spieler"), relief=tk.RIDGE)
        self.new_player.grid(row=0, column=0, columnspan=2, padx=5, pady=5)
        self.new_player.insert(tk.END, 'neuer Spieler')
        tk.Button(self,
            text="hinzufügen", relief=tk.RIDGE, command=self.add_player
        ).grid(row=0, column=3, padx=5, pady=5)
        tk.Button(self,
            text="lösche Spieler", relief=tk.RIDGE, command=self.game_board.remove_player
        ).grid(row=0, column=4, padx=5, pady=5)

    def add_player(self):
        name = self.new_player.get()
        if name == "neuer Spieler":
            self.new_player.config(fg="RED")
        else:
            self.new_player.config(fg="black")
            self.new_player.delete(0, tk.END)
            self.new_player.insert(0, "neuer Spieler")
            self.game_board.add_player(name)


class GameBoard(tk.Frame):
    NAMES = ["Legende", "1: ", "2: ", "3: ", "4: ", "5: ", "6:",
            "Bonus:", "Summe:","Full House: ", "kleine Straße:",
            "große Straße:", "dreier Pasch:", "vierer Pasch:", "Chance:",
            "Kniffel:", "Summe:", "Total:"]
    def __init__(self, master):
        super().__init__(master, bg=DARK)
        self.players = list()
        for i, name in enumerate(self.NAMES):
            tk.Label(self,
                text=name, width=11, bg=DARK2, fg="white", relief=tk.GROOVE
            ).grid(column=0, row=i, sticky="e", padx=15, pady=5)

    def add_player(self, name):
        player = list()
        self.players.append(player)
        player.append(tk.Label(self, text=name, fg="white", bg=DARK2))
        # Augen
        for _ in range(6):
            player.append(tk.Entry(self, width=max(8, len(name)), bg=DARK2, fg="white"))
        # Summe, Bonus
        player.append(tk.Label(self, text=" - ", fg="white", width=max(8, len(name)), bg=DARK2, relief=tk.GROOVE))
        player.append(tk.Label(self, text=" - ", fg="white", width=max(8, len(name)), bg=DARK2, relief=tk.GROOVE))
        # spezielle Würfe
        for _ in range(7):
            player.append(tk.Entry(self, width=max(8, len(name)), bg=DARK2, fg="white"))
        # Summe, Total
        player.append(tk.Label(self, text=" - ", fg="white", width=max(8, len(name)), bg=DARK2, relief=tk.GROOVE))
        player.append(tk.Label(self, text=" - ", fg="white", width=max(8, len(name)), bg=DARK2, relief=tk.GROOVE))

        for i, widget in enumerate(player):
            widget.grid(column=len(self.players), row=i)

    def remove_player(self):
        if self.players:
            player = self.players.pop()
            for widget in player:
                widget.destroy()

    def sum_up(self):
        def sum_up_entries(entries):
            summe = 0
            for entry in entries:
                value = entry.get()
                if value.isdigit():
                    summe += int(value)
                    entry.config(bg=DARK2)
                elif not value:
                    entry.config(bg=DARK2)
                else:
                    entry.config(bg="RED")
            return summe
        
        for player_scores in self.players:
            summe1 = sum_up_entries(player_scores[1:7])
            summe2 = sum_up_entries(player_scores[9:16])
            bonus = 0 if summe1 < 63 else 25
            player_scores[7].config(text=summe1)
            player_scores[8].config(text=bonus)
            player_scores[16].config(text=summe2)
            player_scores[17].config(text=summe1 + summe2 + bonus)

    def reset_points(self):
        for player_scores in self.players:
            for entry in player_scores[1:7]:
                entry.delete(0, tk.END)
            for entry in player_scores[9:16]:
                entry.delete(0, tk.END)
            player_scores[7].config(text="-")
            player_scores[8].config(text="-")
            player_scores[16].config(text="-")
            player_scores[17].config(text="-")


if __name__ == '__main__':
    main()
Caskuda
User
Beiträge: 26
Registriert: Sonntag 15. März 2020, 22:09

Hallo Sirius,

tausend Dank für das ausführliche Review und dein Codebeispiel. Die expliziten Abhängigkeiten der Klassen anzugeben ist natürlich deutlich eleganter, als das, was ich verbrochen habe.

Zu Zeile 13,14: Leuchtet ein. Ich spare dadurch etliche Wiederholungen.
Zu Zeile 16: ist gelöscht.
Zu Zeile 27ff, 51ff: habe es umgestellt und nun alles in einer einzelnen Klasse zusammengefasst.
Nach Übergabe der expliziten Abhängigkeiten haben die Klassen GameFlow und PlayerManagement, sowie Kniffel selbst eigentlich kaum
Mehrwert.
Zu Zeile 126: Ja, ist in der Tat deutlich besser lesbar.
Zu Zeile 157: Danke an die Erinnerung. Ich hatte .isdigit() nicht mehr im Hinterkopf.
Zu Zeile 175ff: Habe die Spieler-Widgets in einem Dictionary organisiert, über das ich nun zugreife.


Überarbeitete Variante:
- Farbliches Hervorheben falscher Eingaben.
- Zugriff auf Punkte über Dictionary

Code: Alles auswählen

import tkinter as tk
import re

LIGHT = "#468DAE"
DARK = "#0F668E"
DARK2 = "#093E62"


def main():
    game = Kniffel()
    game.mainloop()

class Kniffel(tk.Tk):
    def __init__(self):
        super().__init__()
        self.geometry("500x680")
        self.config(bg=DARK2)
        self.title("Kniffel")

        # etwas hässlich: gehört überarbeitet
        NAMES = ["Name:", "1:", "2:", "3:", "4:", "5:", "6:",
                "Bonus:", "Summe:","Full House:", "kleine Straße:",
                "große Straße:", "dreier Pasch:", "vierer Pasch:", "Chance:",
                "Kniffel:", "Summe2:", "Total:"]

        self.entry_fields =  {"1:" : set(range(1, 6)),
                              "2:" : set(range(2, 11, 2)),
                              "3:" : set(range(3, 16, 3)),
                              "4:" : set(range(4, 21, 4)),
                              "5:" : set(range(5,26,5)),
                              "6:" : set(range(6,31,6)),
                              "Full House:" : {0, 25},
                              "kleine Straße:" : {0, 30},
                              "große Straße:" : {0, 40},
                              "dreier Pasch:" : set(range(5,31)),
                              "vierer Pasch:" : set(range(5,31)),
                              "Chance:" : set(range(5,31)),
                              "Kniffel:" : {0, 50}}

        # -- Spielerverwaltung --
        self.new_player = tk.Entry(self, width=len("neuer Spieler"),
                                   relief=tk.RIDGE)
        self.add_a_player = tk.Button(self, text="hinzufügen",
                               relief=tk.RIDGE, command=self.add_player)
        self.delete_player = tk.Button(self, text="lösche Spieler",
                                relief=tk.RIDGE, command=self.remove_player)

        # -- Beschreibung --
        self.new_player.grid(row=0, column=0, padx=5, pady=5)
        self.new_player.insert(tk.END, 'neuer Spieler')
        self.add_a_player.grid(row=0, column=1, padx=5, pady=5)
        self.delete_player.grid(row=0, column=2, padx=5, pady=5)

        for i, name in enumerate(NAMES):
            description = tk.Label(self, text=name, width=11,
                                   bg=DARK2, fg="white", relief = tk.GROOVE)
            description.grid(column=0, row=i+1, sticky="e", padx=15, pady=5)
        self.players = list()

        # -- Punkteauswertung --
        self.get_points = tk.Button(self, text="Ergebnis",
                                    relief=tk.RIDGE, command=self.sum_up)
        self.reset = tk.Button(self, text="zurücksetzen",
                               relief=tk.RIDGE, command=self.reset_points)

        self.get_points.grid(row=20, column=0, columnspan=1, padx=5, pady=5)
        self.reset.grid(row=20, column=1, columnspan=1, padx=5, pady=5)

    def add_player(self):
        if self.new_player.get() in {"neuer Spieler", ""}:
            self.new_player.delete(0, tk.END)
            self.new_player.config(fg="red")
            self.new_player.insert(0, "neuer Spieler")
        else:
            name = self.new_player.get()
            self.players.append(dict())
            player = self.players[-1]
            player['Name:'] = tk.Label(self, text=name, relief=tk.GROOVE,
                                       fg="white", bg=DARK2)
            length = len(name)
            for name in self.entry_fields.keys():
                player[name] = tk.Entry(self, width=max(8, length),
                                        bg=DARK2, fg="white")
            for name in ["Bonus:", "Summe:", "Summe2:", "Total:"]:
                player[name] = (tk.Label(self, text="-", fg ="white",
                                        width=max(8, length),
                                        bg=DARK2, relief=tk.GROOVE))
            # -- Layout --
            column = len(self.players) # first column is the description
            for i, key in enumerate(self.players[-1]):
                player[key].grid(row=i+1, column=column)

            self.new_player.delete(0, tk.END)
            self.new_player.config(fg="black")
            self.new_player.insert(0, "neuer Spieler")

    def remove_player(self):
        if len(self.players) > 0:
            player = self.players.pop()
            for widget in player:
                player.get(widget).destroy()

    def sum_up(self):
        for player in self.players:
            summe1 = 0
            for key in ["1:", "2:", "3:", "4:", "5:", "6:"]:
                summe1 += self.evaluate(player, key)
            bonus = 0 if summe1 < 63 else 25
            summe1 += bonus
            player["Bonus:"].config(text=bonus)
            player["Summe:"].config(text=summe1)

            summe2 = 0
            for key in ["dreier Pasch:", "vierer Pasch:", "Chance:",
                        "Full House:", "kleine Straße:", "große Straße:",
                        "Kniffel:"]:
                summe2 += self.evaluate(player, key)
            player["Summe2:"].config(text=summe2)
            player["Total:"].config(text=summe1+summe2)

    def evaluate(self, player, key):
        value = player[key].get()
        numbers = self.entry_fields.get(key)
        if self.is_valid(value, numbers):
            player[key].config(bg=DARK2)
            return int(value)
        else:
            player[key].config(bg="red")
            return 0

    def is_valid(self, value, menge):
        if value.isdigit() and int(value) in menge:
            return True
        return False

    def reset_points(self):
        for player in self.players:
            for name in self.entry_fields:
                player[name].delete(0, tk.END)
                player[name].config(bg=DARK)
                player[name].insert(0, "")
            for name in ["Summe:", "Bonus:", "Summe2:", "Total:"]:
                player[name].config(text="-")

if __name__ == '__main__':
    main()
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Zeile 21,26: Konstanten werden nicht innerhalb von Methoden definiert. NAMES und ENTRY_FIELDS gehören aus __init__.
Zeile 26: man sollte nicht so einrücken, dass Klammern passen, denn das beudetet, dass man bei jeder Änderung die Einrückung anpassen muß. Ich Rücke immer 4 Leerzeichen ein.
Zeile 43f, 61f: nicht alles müssen Attribute sein.
Zeile 76/77: erst erzeugt man ein Objekt, dann hängt man es an eine Liste
Zeile 85: da ist ein Klammernpaar zu viel
Zeile 89: player
Zeile 130: is_valid braucht kein `self`, gehört also nicht in die Klasse. Man kann den Wahrheitswert gleich ohne if zurückgeben.

Code: Alles auswählen

def is_valid(value, menge):
        return value.isdigit() and int(value) in menge
Caskuda
User
Beiträge: 26
Registriert: Sonntag 15. März 2020, 22:09

Ich mache mich gleich ans Anpassen.


Hinsichtlich des Einrückens:
Rückst du in der nachfolgenden Zeile 4 Leerzeichen tiefer ein, oder 4 Leerzeichen hinter beginn der Klammer?

Verstehe ich das so richtig:

Code: Alles auswählen

example_button = tk.Label(self, 
    text="Ein Button als Beispiel", 
    relief=tk.GROOVE,
    fg="blue",
    bg="black").grid(
    row=0,
    column=7,
    columnspan=2)
Caskuda
User
Beiträge: 26
Registriert: Sonntag 15. März 2020, 22:09

Nochmals tausend Dank von mir, für deine Hilfestellung.
Zeile 21,26: Konstanten werden nicht innerhalb von Methoden definiert. NAMES und ENTRY_FIELDS gehören aus __init__.
Zeile 26: man sollte nicht so einrücken, dass Klammern passen, denn das beudetet, dass man bei jeder Änderung die Einrückung anpassen muß. Ich Rücke immer 4 Leerzeichen ein.
Zeile 43f, 61f: nicht alles müssen Attribute sein.
Zeile 76/77: erst erzeugt man ein Objekt, dann hängt man es an eine Liste
Zeile 85: da ist ein Klammernpaar zu viel
Zeile 89: player
Zeile 130: is_valid braucht kein `self`, gehört also nicht in die Klasse. Man kann den Wahrheitswert gleich ohne if zurückgeben.
zu Zeile 21, 26: Danke, ich stimme voll und ganz zu. Wenn ich es in der __init__ stehen
lasse, ist es nur in deren Scope definiert und ich habe in keiner anderen Methode
der Klasse Zugriff darauf.
Sollte ich die Farbwerte hier ggf. auch nur in der Klasse global definieren?

zu Zeile 43f, 61f : angepasst. Nur noch das Entry-Feld new_player und die Liste
der Spieler habe ich als Attribute belassen.

zu Zeile 76/77 : angepasst
zu Zeile 85: angepasst
zu Zeile 89: angepasst

zu Zeile 130: die Funktion habe ich komplett rausgeschmissen. Dein Beispiel sagt hier schon alles.
Ich hatte mit der Funktion nur den Wahrheitswert zurückgegeben, den ich in der If-Bedingung prüfe...
Ich benutze nun diese Bedingung direkt innerhalb der evaluate - Funktion.

Code: Alles auswählen

import tkinter as tk

LIGHT = "#468DAE"
DARK = "#093E62"


def main():
    game = Kniffel()
    game.mainloop()

class Kniffel(tk.Tk):
    NAMES = ["Name:", "1:", "2:", "3:", "4:", "5:", "6:",
        "Bonus:", "Summe:","Full House:", "kleine Straße:",
        "große Straße:", "3er Pasch:", "4er Pasch:", "Chance:",
        "Kniffel:", "Summe2:", "Total:"]

    ENTRY_FIELDS =  {"1:" : set(range(1, 6)),
        "2:" : set(range(2, 11, 2)),
        "3:" : set(range(3, 16, 3)),
        "4:" : set(range(4, 21, 4)),
        "5:" : set(range(5,26,5)),
        "6:" : set(range(6,31,6)),
        "Full House:" : {0, 25},
        "kleine Straße:" : {0, 30},
        "große Straße:" : {0, 40},
        "3er Pasch:" : set(range(5,31)),
        "4er Pasch:" : set(range(5,31)),
        "Chance:" : set(range(5,31)),
        "Kniffel:" : {0, 50}}

    def __init__(self):
        super().__init__()
        self.geometry("500x680")
        self.config(bg=DARK)
        self.title("Kniffel")

        # -- Spielerverwaltung --
        self.new_player = tk.Entry(self, width=len("neuer Spieler"),
            relief=tk.RIDGE)
        self.new_player.grid(row=0, column=0, padx=5, pady=5)
        self.new_player.insert(tk.END, 'neuer Spieler')
        tk.Button(self, text="hinzufügen", relief=tk.RIDGE,
            command=self.add_player).grid(row=0, column=1, padx=5, pady=5)
        tk.Button(self, text="lösche Spieler", relief=tk.RIDGE,
            command=self.remove_player).grid(row=0, column=2, padx=5, pady=5)

        # -- Tabelle --
        for i, name in enumerate(Kniffel.NAMES):
            tk.Label(self, text=name, width=11, bg=DARK,
                fg="white", relief = tk.GROOVE).grid(column=0, row=i+1,
                sticky="e", padx=15, pady=5)

        self.players = list()

        # -- Punkteauswertung --
        tk.Button(self, text="Ergebnis",
            relief=tk.RIDGE, command=self.sum_up).grid(row=20, column=0,
            columnspan=1, padx=5, pady=5)
        tk.Button(self, text="zurücksetzen",
            relief=tk.RIDGE, command=self.reset_points).grid(row=20, column=1,
            columnspan=1, padx=5, pady=5)

    def add_player(self):
        if self.new_player.get() in {"neuer Spieler", ""}:
            self.new_player.delete(0, tk.END)
            self.new_player.config(fg="red")
            self.new_player.insert(0, "neuer Spieler")
        else:
            name = self.new_player.get()
            player = {name : None for name in Kniffel.NAMES}
            player['Name:'] = tk.Label(self, text=name, relief=tk.GROOVE,
                fg="white", bg=DARK)
            length = len(name)
            for name in Kniffel.ENTRY_FIELDS.keys():
                player[name] = tk.Entry(self, width=max(8, length),
                    bg=DARK, fg="white")
            for name in ["Bonus:", "Summe:", "Summe2:", "Total:"]:
                player[name] = tk.Label(self, text="-", fg ="white",
                    width=max(8, length), bg=DARK, relief=tk.GROOVE)

            # -- Layout --
            column = len(self.players) + 1
            for i, key in enumerate(player):
                player[key].grid(row=i+1, column=column)
            self.players.append(player)

            self.new_player.delete(0, tk.END)
            self.new_player.config(fg="black")
            self.new_player.insert(0, "neuer Spieler")

    def remove_player(self):
        if len(self.players) > 0:
            player = self.players.pop()
            for widget in player:
                player.get(widget).destroy()

    def sum_up(self):
        for player in self.players:
            summe1 = 0
            for key in ["1:", "2:", "3:", "4:", "5:", "6:"]:
                summe1 += self.evaluate(player, key)
            bonus = 0 if summe1 < 63 else 25
            summe1 += bonus
            player["Bonus:"].config(text=bonus)
            player["Summe:"].config(text=summe1)

            summe2 = 0
            for key in ["3er Pasch:", "4er Pasch:", "Chance:",
                "Full House:", "kleine Straße:", "große Straße:", "Kniffel:"]:
                summe2 += self.evaluate(player, key)
            player["Summe2:"].config(text=summe2)
            player["Total:"].config(text=summe1+summe2)

    def evaluate(self, player, key):
        value = player[key].get()
        numbers = Kniffel.ENTRY_FIELDS.get(key)
        if value.isdigit() and int(value) in numbers:
            player[key].config(bg=DARK)
            return int(value)
        else:
            player[key].config(bg="red")
            return 0

    def reset_points(self):
        for player in self.players:
            for name in Kniffel.ENTRY_FIELDS:
                player[name].delete(0, tk.END)
                player[name].config(bg=DARK)
                player[name].insert(0, "")
            for name in ["Summe:", "Bonus:", "Summe2:", "Total:"]:
                player[name].config(text="-")


if __name__ == '__main__':
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 14085
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Caskuda: Ich rücke, falls es nicht in die Zeile passt, auch das erste Argument ein, also in einer eigenen Zeile und setze die schliessende Klammer auch in eine eigene Zeile, und zwar nicht eingerückt. Bei Deinem Beispiel sieht man nicht wirklich leicht, dass das gar nicht alles Argumente von `Label` sind, sondern da noch ein `grid()`-Aufruf folgt. Also so:

Code: Alles auswählen

tk.Label(
    self,
    text="Ein Button als Beispiel",
    relief=tk.GROOVE,
    fg="blue",
    bg="black",
).grid(row=0, column=7, columnspan=2)
Wobei es keinen Sinn macht `None` an einen Namen zu binden, darum habe ich den weggelassen.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Caskuda
User
Beiträge: 26
Registriert: Sonntag 15. März 2020, 22:09

Lieben Dank __blackjack__,
habe den Wust an Argumenten angepasst.

Code: Alles auswählen

import tkinter as tk


def main():
    game = Kniffel()
    game.mainloop()

class Kniffel(tk.Tk):
    DARK = "#093E62"

    NAMES = [
        "Name:",
        "1:",
        "2:",
        "3:",
        "4:",
        "5:",
        "6:",
        "Bonus:",
        "Summe:",
        "Full House:",
        "kleine Straße:",
        "große Straße:",
        "3er Pasch:",
        "4er Pasch:",
        "Chance:",
        "Kniffel:",
        "Summe2:",
        "Total:"
    ]

    ENTRY_FIELDS =  {"1:" : set(range(1, 6)),
        "2:" : set(range(2, 11, 2)),
        "3:" : set(range(3, 16, 3)),
        "4:" : set(range(4, 21, 4)),
        "5:" : set(range(5,26,5)),
        "6:" : set(range(6,31,6)),
        "Full House:" : {0, 25},
        "kleine Straße:" : {0, 30},
        "große Straße:" : {0, 40},
        "3er Pasch:" : set(range(5,31)),
        "4er Pasch:" : set(range(5,31)),
        "Chance:" : set(range(5,31)),
        "Kniffel:" : {0, 50}}

    def __init__(self):
        super().__init__()
        self.geometry("500x680")
        self.config(bg=Kniffel.DARK)
        self.title("Kniffel")

        # -- Spielerverwaltung --
        self.new_player = tk.Entry(
            self,
            relief=tk.RIDGE,
            width=len("neuer Spieler")
        )
        self.new_player.grid(row=0, column=0, padx=5, pady=5)
        self.new_player.insert(tk.END, 'neuer Spieler')
        tk.Button(
            self,
            command=self.add_player,
            relief=tk.RIDGE,
            text="hinzufügen"
        ).grid(row=0, column=1, padx=5, pady=5)

        tk.Button(
            self,
            command=self.remove_player,
            relief=tk.RIDGE,
            text="lösche Spieler",
        ).grid(row=0, column=2, padx=5, pady=5)

        # -- Tabelle --
        for i, name in enumerate(Kniffel.NAMES):
            tk.Label(
                self,
                bg=Kniffel.DARK,
                fg="white",
                relief = tk.GROOVE,
                text=name,
                width=11
            ).grid(column=0, row=i+1, sticky="e", padx=15, pady=5)

        self.players = list()

        # -- Punkteauswertung --
        tk.Button(
            self,
            text="Ergebnis",
            relief=tk.RIDGE,
            command=self.sum_up
        ).grid(row=20, column=0, padx=5, pady=5)

        tk.Button(
            self,
            command=self.reset_points,
            relief=tk.RIDGE,
            text="zurücksetzen"
        ).grid(row=20, column=1, padx=5, pady=5)

    def add_player(self):
        if self.new_player.get() in {"neuer Spieler", ""}:
            self.new_player.delete(0, tk.END)
            self.new_player.config(fg="red")
            self.new_player.insert(0, "neuer Spieler")
        else:
            name = self.new_player.get()
            player = {name : None for name in Kniffel.NAMES}
            player['Name:'] = tk.Label(
                self,
                bg=Kniffel.DARK,
                fg="white",
                relief=tk.GROOVE,
                text=name
            )

            length = len(name)
            for name in Kniffel.ENTRY_FIELDS.keys():
                player[name] = tk.Entry(
                    self,
                    bg=Kniffel.DARK,
                    fg="white",
                    width=max(8, length)
                )

            for name in ["Bonus:", "Summe:", "Summe2:", "Total:"]:
                player[name] = tk.Label(
                    self,
                    bg=Kniffel.DARK,
                    fg ="white",
                    relief=tk.GROOVE,
                    text="-",
                    width=max(8, length)
                )

            # -- Layout --
            column = len(self.players) + 1
            for i, key in enumerate(player):
                player[key].grid(column=column, row=i+1)
            self.players.append(player)

            self.new_player.delete(0, tk.END)
            self.new_player.config(fg="black")
            self.new_player.insert(0, "neuer Spieler")

    def remove_player(self):
        if len(self.players) > 0:
            player = self.players.pop()
            for widget in player:
                player.get(widget).destroy()

    def sum_up(self):
        for player in self.players:
            summe1 = 0
            for key in ["1:", "2:", "3:", "4:", "5:", "6:"]:
                summe1 += self.evaluate(player, key)
            bonus = 0 if summe1 < 63 else 25
            summe1 += bonus
            player["Bonus:"].config(text=bonus)
            player["Summe:"].config(text=summe1)

            summe2 = 0
            for key in [
                "3er Pasch:",
                "4er Pasch:",
                "Chance:",
                "Full House:",
                "kleine Straße:",
                "große Straße:",
                "Kniffel:"
            ]:
                summe2 += self.evaluate(player, key)

            player["Summe2:"].config(text=summe2)
            player["Total:"].config(text=summe1+summe2)

    def evaluate(self, player, key):
        value = player[key].get()
        numbers = Kniffel.ENTRY_FIELDS.get(key)
        if value.isdigit() and int(value) in numbers:
            player[key].config(bg=Kniffel.DARK)
            return int(value)
        else:
            player[key].config(bg="red")
            return 0

    def reset_points(self):
        for player in self.players:
            for name in Kniffel.ENTRY_FIELDS:
                player[name].delete(0, tk.END)
                player[name].config(bg=Kniffel.DARK)
                player[name].insert(0, "")
            for name in ["Summe:", "Bonus:", "Summe2:", "Total:"]:
                player[name].config(text="-")


if __name__ == '__main__':
    main()


Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Schlecht ist jetzt noch, dass die Namen der Felder drei mal quer über den Code verstreut wiederholt werden. Auf Klassenkonstenten geift man auch über self zu. In add_player erzeugst Du ein Wörterbuch mit Dummy-Werten. Sowas macht man nicht, weil Du ja danach gleich wieder die Dummy-Werte überschreibst.

Code: Alles auswählen

import tkinter as tk
from itertools import chain

def main():
    game = Kniffel()
    game.mainloop()


class Kniffel(tk.Tk):
    DARK = "#093E62"
    ENTRIES_BLOCK1 = {
        "1:": set(range(1, 6)),
        "2:": set(range(2, 11, 2)),
        "3:": set(range(3, 16, 3)),
        "4:": set(range(4, 21, 4)),
        "5:": set(range(5,26,5)),
        "6:": set(range(6,31,6)),
    }
    ENTRIES_BLOCK2 = {
        "Full House:": {0, 25},
        "kleine Straße:": {0, 30},
        "große Straße:": {0, 40},
        "3er Pasch:": set(range(5,31)),
        "4er Pasch:": set(range(5,31)),
        "Chance:": set(range(5,31)),
        "Kniffel:": {0, 50}
    }

    LABELS = ["Summe:", "Bonus:", "Summe2:", "Total:"]

    NAMES = [
        "Name:",
        *ENTRIES_BLOCK1,
        "Bonus:",
        "Summe:",
        *ENTRIES_BLOCK2,
        "Summe2:",
        "Total:"
    ]

    def __init__(self):
        super().__init__()
        self.config(bg=self.DARK)
        self.title("Kniffel")

        # -- Spielerverwaltung --
        self.new_player = tk.Entry(
            self,
            relief=tk.RIDGE,
            width=len("neuer Spieler")
        )
        self.new_player.grid(row=0, column=0, padx=5, pady=5)
        self.new_player.insert(tk.END, 'neuer Spieler')
        tk.Button(
            self,
            command=self.add_player,
            relief=tk.RIDGE,
            text="hinzufügen"
        ).grid(row=0, column=1, padx=5, pady=5)

        tk.Button(
            self,
            command=self.remove_player,
            relief=tk.RIDGE,
            text="lösche Spieler",
        ).grid(row=0, column=2, padx=5, pady=5)

        # -- Tabelle --
        for i, name in enumerate(self.NAMES):
            tk.Label(
                self,
                bg=self.DARK,
                fg="white",
                relief = tk.GROOVE,
                text=name,
                width=11
            ).grid(column=0, row=i+1, sticky="e", padx=15, pady=5)

        self.players = list()

        # -- Punkteauswertung --
        tk.Button(
            self,
            text="Ergebnis",
            relief=tk.RIDGE,
            command=self.sum_up
        ).grid(row=20, column=0, padx=5, pady=5)

        tk.Button(
            self,
            command=self.reset_points,
            relief=tk.RIDGE,
            text="zurücksetzen"
        ).grid(row=20, column=1, padx=5, pady=5)

    def add_player(self):
        if self.new_player.get() in {"neuer Spieler", ""}:
            self.new_player.config(fg="red")
        else:
            name = self.new_player.get()
            player = {}
            player['Name:'] = tk.Label(
                self,
                bg=Kniffel.DARK,
                fg="white",
                relief=tk.GROOVE,
                text=name
            )

            length = len(name)
            for name in chain(self.ENTRIES_BLOCK1, self.ENTRIES_BLOCK2):
                player[name] = tk.Entry(
                    self,
                    bg=Kniffel.DARK,
                    fg="white",
                    width=max(8, length)
                )

            for name in self.LABELS:
                player[name] = tk.Label(
                    self,
                    bg=Kniffel.DARK,
                    fg ="white",
                    relief=tk.GROOVE,
                    text="-",
                    width=max(8, length)
                )

            # -- Layout --
            column = len(self.players) + 1
            for row, key in enumerate(self.NAMES, 1):
                player[key].grid(column=column, row=row)
            self.players.append(player)

            self.new_player.config(fg="black")
        self.new_player.delete(0, tk.END)
        self.new_player.insert(0, "neuer Spieler")

    def remove_player(self):
        if self.players:
            player = self.players.pop()
            for widget in player.values():
                widget.destroy()

    def sum_up(self):
        for player in self.players:
            summe1 = sum(
                self.evaluate(player[key], numbers)
                for key, numbers in self.ENTRIES_BLOCK1.items()
            )
            bonus = 0 if summe1 < 63 else 25
            summe1 += bonus
            player["Bonus:"].config(text=bonus)
            player["Summe:"].config(text=summe1)

            summe2 = sum(
                self.evaluate(player[key], numbers)
                for key, numbers in self.ENTRIES_BLOCK2.items()
            )
            player["Summe2:"].config(text=summe2)
            player["Total:"].config(text=summe1+summe2)

    def evaluate(self, entry, numbers):
        value = entry.get()
        if value.isdigit() and int(value) in numbers:
            entry.config(bg=self.DARK)
            return int(value)
        else:
            entry.config(bg="red")
            return 0

    def reset_points(self):
        for player in self.players:
            for name in chain(self.ENTRIES_BLOCK1, self.ENTRIES_BLOCK2):
                player[name].delete(0, tk.END)
                player[name].config(bg=Kniffel.DARK)
            for name in self.LABELS:
                player[name].config(text="-")


if __name__ == '__main__':
    main()
Caskuda
User
Beiträge: 26
Registriert: Sonntag 15. März 2020, 22:09

Vielen lieben Dank, Sirius.
Deine Hilfestellungen sind wirklich unglaublich gut und haben einen immensen Lerneffekt.


Ich gehe dein Codebeispiel momentan durch.
Das Aufteilen des Entry-Dictionaries auf die beiden Entry-Blöcke, macht den Code später wirklich deutlich
lesbarer.

Auch hast du die Wiederholung von .delete(), .insert() bei add_player() ausgemerzt.

Ich werde als nächstes noch ein optionales Fenster als Würfelbecher einbauen.

Falls ich das heute nicht mehr schaffe, wünsche ich vorweg schon einmal einen guten Start ins Wochenende.
Antworten