mehrere Buttons mit Bildern

Fragen zu Tkinter.
Antworten
audacity363
User
Beiträge: 83
Registriert: Dienstag 6. August 2013, 18:59

Mahlzeit
Ich müsste ein komplettes Kartenspiel in einem Fenster anzeigen und diese müssen auch noch anklickbar sein. Also habe ich ein wenig gegoogelt und herausgefunden wie man Bilder auf einen Button bindet.
Bei einem funktioniert das auch noch ganze gut aber sobald ich einen zweiten hinzufüge wird der erste ausgeraut und ist auch nicht mehr anklickbar.
Habe euch mal eine Hardcopy hochgeladen damit ihr seht wie es aussieht: http://www.directupload.net/file/d/3573 ... wa_png.htm

Der Code:

Code: Alles auswählen

from Tkinter import *
import Image, ImageTk

import tkMessageBox

def antwort():
    tkMessageBox.showinfo("Test", "Button1")
    
def antwort1():
    tkMessageBox.showinfo("Test", "Button2")
     
root = Tk()
root.title("Kartenspiel")

image = ImageTk.PhotoImage(Image.open("bilder/1_10.bmp"))
Button(root,image = image, command = antwort).grid(row=0, column=0)

image = ImageTk.PhotoImage(Image.open("bilder/1_11.bmp"))
Button(root,image = image, command = antwort1).grid(row=0, column=1)

root.mainloop()
BlackJack

@audacity363: Du musst auf Python-Seite Referenzen auf die `PhotoImage`-Exemplare behalten, sonst werden die wieder freigegeben und Tk kann nicht mehr darauf zugreifen um sie anzuzeigen. Eine einfache Möglichkeit ist immer das Bild als Attribut an das Widget-Objekt zu binden auf dem es angezeigt wird. Aber in diesem Fall würde es sich anbieten die Kartenbilder vielleicht in eine Datenstruktur zu laden. Zum Beispiel eine Abbildung von der logischen Darstellung der Karten auf die `PhotoImage`-Exemplare.
audacity363
User
Beiträge: 83
Registriert: Dienstag 6. August 2013, 18:59

Okey Danke hat funktioniert.
Gibt es eine Möglichkeit der Methode, die von dem Button aufgerufen wird, Parameter zu übergeben? Z.B. die ID des Buttons?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Schau dir dazu mal im Modul functools die Funktion partial an.
Das Leben ist wie ein Tennisball.
audacity363
User
Beiträge: 83
Registriert: Dienstag 6. August 2013, 18:59

Na ja okey aber das bringt es nicht wirklich, da ich beim Parameter "command" ja gar keine Parameter übergeben kann.
Bei Java ist es ja z.B. Möglich einen ClickListner zu definieren und dort wird dann die ID des Buttons mitgegeben. Dementsprechend kann man dann abgleichen.
BlackJack

@audacity363: Doch das bringt es wirklich denn Du kannst damit Funktionen aus anderen Funktionen oder Methoden erstellen und angeben mit welchen Argumenten die aufgerufen werden sollen. Also genau das was Du haben wolltest.

Code: Alles auswählen

In [3]: def f(spam):
   ...:     print spam
   ...: 

In [4]: g = partial(f, 42)

In [5]: g()
42
audacity363
User
Beiträge: 83
Registriert: Dienstag 6. August 2013, 18:59

ja mir ist kla wie es funktioniert aber so müsste ich 52x das ganze "redefinen" und dann nochmal in der Funktion abfragen was denn jetzt übergeben wurde.
Erschwerend kommt noch dazu, dass ich die Buttons in zwei For Schleifen per .grid() verteile und dadurch immer die gleiche Funktion aufrufe. Daher die Frage ob man nicht einen "ClickListener" definieren kann bei dem die z.B. die ID des Buttons übergeben wird und die dann verglichen werden kann.
BlackJack

@audacity363: Was meinst Du mit 52 mal „redefinen”? Und wo musst Du was überprüfen? `f` ist in dem Beispiel der ”ClickListener” und `g` ist jetzt nur für das Beispiel. Das Ergebnis von `partial()` würde man direkt als `command`-Argument übergeben und nicht noch mal extra an einen Namen binden. Du willst doch eine Funktion ohne Argument die man als `command` übergeben kann, und die wenn man sie Aufruft, eine andere Funktion/Methode mit einem festen Wert/Id als Argument aufruft. Genau so etwas kannst Du mit `partial()` machen.
audacity363
User
Beiträge: 83
Registriert: Dienstag 6. August 2013, 18:59

OKey danke ich wusse nicht, dass man dies auch direkt dahinter schreiben kann. Nur bleibt immer noch das Problem, dass die Buttons keine eigene "ID" haben:

Code: Alles auswählen

def karten_suchen():
    i = 0
    for x in range(1,5):
        for y in range(1, 15):
            path = "bilder/" + str(x) + "_" + str(y) + ".bmp"
            print path
            try:
                image[i] = ImageTk.PhotoImage(Image.open(path).resize((100, 150), Image.ANTIALIAS))
                #image[i].resize((100, 150), Image.ANTIALIAS)
                button[i] = Button(root,image = image[i], command = lambda: test(str(x), str(y)))
                button[i].grid(row=x, column=y)
                i += 1
                
            except IOError:
                print "Nicht gefunden"
Damit ich nicht alles einzeln schreiben muss mache ich es halt in einer Schleife. Als X und Y Wert nimmt er nun immer 4 und 14, was ja kla ist, da er ja erst nach dem Durchlauf darauf zugreift.
BlackJack

@audacity363: Natürlich kann man statt ``a = f(42); g(a)`` auch immer ``g(f(42))`` schreiben.

In Deinem Beispiel hast Du jetzt aber auch nicht `partial()` verwendet. Mach das mal, dann funktioniert das auch.

`image` und `button` sind eigenartig. Wo kommen die her und was sind denn da vorher für Werte drin? Oder sind das Wörterbücher statt Listen? Falls ja, warum? Die Werte stehen ausserdem für mehrere Bilder/Schaltflächen, das sollte man im Namen durch Mehrzahl ausdrücken, also `images` und `buttons`.

`root` kommt auch einfach so aus dem Nichts. Werte, ausser Konstanten, die man in Funktionen oder Methoden verwendet, sollten als Argumente übergeben werden. Funktionen sollten in sich geschlossene Aufgaben wahrnehmen und nicht über globale Datenstrukturen verwoben sein.

`i` sollte man nicht ”per Hand” hochzählen, sondern `enumerate()` verwenden.

Pfade setzt man besser mit `os.path.join()` zusammen und Werte und Zeichenketten mit der `format()`-Methode statt `str()` und ``+``.
Antworten