Bild Button vergleichen

Fragen zu Tkinter.
Antworten
Troxon
User
Beiträge: 3
Registriert: Donnerstag 24. Juni 2021, 15:35

Hallo liebe Python Forum Community,
ich möchte das Spiel Memory als Programmierübung programmieren, dabei soll das Spiel sich nicht vom "reallife" Spiel unterscheiden. Wenn ich einen Button drücke soll das Bild auf ihm geändert werden und wenn ich
ihn nochmal drücke soll das Rückenbild wieder angezeigt werden. Ich habe die Zeilen um die es sich handelt mit ? markiert, hab nur zum Verständnis den ganzen Code Kopiert. Ich weiß nicht ob ich Logikfehler habe
oder was sonst das Problem sein könnte, habe es auch schon ohne Klasse in einer Funktion probiert, weil ich das erste mal mit Klassen arbeite.
Ich würde mich über Rückmeldung freuen,
Grüße Troxon

Code: Alles auswählen

import tkinter as tk

root = tk.Tk()
root.geometry("850x850")
root.config(background="black")

apfel_image = tk.PhotoImage(file="ApfelBild.png")
astronaut_image = tk.PhotoImage(file="astronaut.png")
bananen_image = tk.PhotoImage(file="BananenBild.png")
deutsche_post_image = tk.PhotoImage(file="DeutschePostLogo.png")
kiwi_image = tk.PhotoImage(file="KiwiBild.png")
science_for_kids_image = tk.PhotoImage(file="ScienceForKidsBild.png")
weißer_hase_image = tk.PhotoImage(file="WeißerHase.png")
world_of_warcraft_image = tk.PhotoImage(file="WorldOfWarcraftLogo.png")
ruecken_image = tk.PhotoImage(file="KartenRuekseite.png")                  # Rückseitenbild


# Bild_Liste mit 16 Bildern jedes zweimal
bild_liste = [apfel_image, apfel_image, astronaut_image, astronaut_image, bananen_image, bananen_image,
              deutsche_post_image, deutsche_post_image,
              kiwi_image, kiwi_image, science_for_kids_image, science_for_kids_image, weißer_hase_image,
              weißer_hase_image, world_of_warcraft_image,
              world_of_warcraft_image]


class button_erzeugung(tk.Button):
    def __init__(self, bild):
        self.bild = bild
        tk.Button.__init__(self, image=ruecken_image, borderwidth=0, command=lambda: self.bild_wechsel(bild))

    def bild_wechsel(function_button, bild):                                                                             ?
        if function_button["image"] == ruecken_image:                                                             ?                                                
            function_button.config(image=bild)                                                                            ?
        else:                                                                                                                                       ?
            function_button.config(image=ruecken_image)                                                        ?



# Erstellung von 16 Objekten mit Bildern aus der bild_liste
button_0 = button_erzeugung(bild_liste[0])
button_0.place(x=10, y=10)
button_1 = button_erzeugung(bild_liste[1])
button_1.place(x=220, y=10)
button_2 = button_erzeugung(bild_liste[2])
button_2.place(x=430, y=10)
button_3 = button_erzeugung(bild_liste[3])
button_3.place(x=640, y=10)
button_4 = button_erzeugung(bild_liste[4])
button_4.place(x=10, y=220)
button_5 = button_erzeugung(bild_liste[5])
button_5.place(x=220, y=220)
button_6 = button_erzeugung(bild_liste[6])
button_6.place(x=430, y=220)
button_7 = button_erzeugung(bild_liste[7])
button_7.place(x=640, y=220)
button_8 = button_erzeugung(bild_liste[8])
button_8.place(x=10, y=430)
button_9 = button_erzeugung(bild_liste[9])
button_9.place(x=220, y=430)
button_10 = button_erzeugung(bild_liste[10])
button_10.place(x=430, y=430)
button_11 = button_erzeugung(bild_liste[11])
button_11.place(x=640, y=430)
button_12 = button_erzeugung(bild_liste[12])
button_12.place(x=10, y=640)
button_13 = button_erzeugung(bild_liste[13])
button_13.place(x=220, y=640)
button_14 = button_erzeugung(bild_liste[14])
button_14.place(x=430, y=640)
button_15 = button_erzeugung(bild_liste[15])
button_15.place(x=640, y=640)


root.tk.mainloop()
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Troxon: Zuerst einmal sollte das nicht alles auf Modulebene stehen. Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Dann brauchen die Bilder keine Namen und man würde nicht von Hand eine Liste hinschreiben, die jedes Bild zweimal enthält, sondern diese Liste aus einer Liste mit den Dateinamen der Bilder programmatisch erstellen.

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.

Man nummeriert keine Namen durch. Man will dann entweder bessere Namen, oder gar keine Einzelnamen, sondern eine Datenstruktur. Oft eine Liste.

Absolute Positions- und Grössenvorgaben funktionieren nicht wirklich. Das mag auf Deinem System so passend aussehen und funktionieren, aber auf anderen Systemen mit anderen Bildschirmgrössen und -auflösungen und Einstellungen kann das komisch aussehen bis hin zu unbenutzbar werden, wenn sich Anzeigeelemente überlappen, oder ausserhalb des vorgegebenen Platzes für die Anzeige befinden.

Zum anordnen in einer Art Gitter ist die `grid()`-Methode da.

`button_erzeugung` ist ein schlechter/falscher Klassenname. Erst einmal werden Klassen in PascalCase benannt, also `ButtonErzeugung` und dann ist das auch ein schräger Name für ein “Ding“. Das beschreibt ja eher einen Vorgang. `BildButton` wäre passender.

Die ``lambda``-Funktion macht keinen Sinn weil die Methode auf das `bild` ja bereits über das Attribut zugriff hat.

Die Methode hat `self` nicht als erstes Argument beziehungsweise hast Du das verwirrenderweise `function_button` genannt.

Wenn Du Dir die beteiligten Werte und Typen bei dem Vergleich mal angeschaut hättest, wäre Dir aufgefallen, dass ``function_button["image"]`` eine Zeichenkette liefert und Zeichenketten immer ungleich Objekten vom Typ `PhotoImage` sind. Man könnte das `PhotoImage` beispielsweise vor dem Vergleichen auch in eine Zeichenkette umwandeln.

Über das `tk`-Attribut des Hauptfensters auf die `mainloop()`-Methode zuzugreifen ist ungewöhnlich. Üblich ist es die entsprechende Methode auf dem Hauptfenster selbst aufzurufen.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import tkinter as tk
from math import sqrt


class BildButton(tk.Button):
    def __init__(self, bild, rueckseitenbild):
        self.bild = bild
        self.rueckseitenbild = rueckseitenbild
        tk.Button.__init__(
            self,
            image=self.rueckseitenbild,
            borderwidth=0,
            command=self.bild_wechseln,
        )

    def bild_wechseln(self):
        if self["image"] == self.rueckseitenbild:
            self.config(image=self.bild)
        else:
            self.config(image=self.rueckseitenbild)


def main():
    root = tk.Tk()
    root.config(background="black")

    bilder = 2 * [
        tk.PhotoImage(file=filename)
        for filename in [
            "ApfelBild.png",
            "astronaut.png",
            "BananenBild.png",
            "DeutschePostLogo.png",
            "KiwiBild.png",
            "ScienceForKidsBild.png",
            "WeißerHase.png",
            "WorldOfWarcraftLogo.png",
        ]
    ]
    rueckseitenbild = tk.PhotoImage(file="KartenRuekseite.png")

    buttons_pro_reihe = int(sqrt(len(bilder)))
    for i, bild in enumerate(bilder):
        row_index, column_index = divmod(i, buttons_pro_reihe)
        BildButton(bild, rueckseitenbild).grid(
            row=row_index, column=column_index
        )

    root.mainloop()


if __name__ == "__main__":
    main()
Neben dem vermischen der Bilder fehlt hier noch eine übergeordnete Instanz, die die Spielregeln/-zustände verwaltet. Also beispielsweise welcher Spieler gerade dran ist, ob eine oder zwei Bilder aufgedeckt sind, bei gleichen Bildern die Bilder vom Spielfeld entfernt, und bei ungleichen die beiden Bilder wieder verdeckt usw.

Man würde auch versuchen die Programm-/Spiellogik von der GUI zu trennen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten