Transparente Bilder in Python einfügen

Fragen zu Tkinter.
BlackJack

@M96Wayne: Das geht nicht. Schalflächen haben immer eine Hintergrundfarbe. Was willst Du denn *eigentlich* damit erreichen?
M96Wayne
User
Beiträge: 22
Registriert: Freitag 20. Juni 2014, 18:18

Wollte einen Pfeil als Button darstellen der über meinem Hintergrundbild liegt. Das Hintergrundbild wechselt ständig, deswegen kann ich den Hintergrund von dem Pfeil auch nicht daran anpassen.
BlackJack

@M96Wayne: Was bedeutet „ständig”? Man könnte schon jedes mal wenn man den Hintergrund wechselt auch ein entsprechendes Bild für die Schaltfläche erstellen.
M96Wayne
User
Beiträge: 22
Registriert: Freitag 20. Juni 2014, 18:18

Ich habe wahrscheinlich 50 oder mehr Hintergrundbilder, die ich da miteinbringen muss, dazu müsste ich dann ~50mal die Hintergrundfläche ändern. Ist auch nicht gerade das Wahre. Hatte gehofft man könnte es ganz Transparent machen. :wink:
BlackJack

@M96Wayne: Wo ist denn das Problem dabei? Wenn man den Hintergrund der Schaltfläche transparent machen könnte, dann passiert doch vom Programmablauf her genau das gleiche. Wenn man ein neues Hintergrundbild setzt, muss die Grafik auf der Schaltfläche neu berechnet werden, damit dort das neue Bild ”durchscheint” Dann gäbe es den Code schon in Tkinter. So musst Du selber ein paar Zeilen schreiben.
M96Wayne
User
Beiträge: 22
Registriert: Freitag 20. Juni 2014, 18:18

Die Problematik dabei ist, dass die Hintergrundbilder verschiedene Farbverläufe haben und man diese nicht als Hintergrundfarbe der Schaltfläche darstellen kann.
Ich wüsste nämlich nicht wie man von einem Bild die genau exakten Farbverläufe auf die Schaltfläche übertragen könnte.
BlackJack

@M96Wayne: Die Hintergrundfarbe der Schaltfläche ist egal, Du setzt da ein Bild ohne Transparenz drauf, denn das Hintergrundbild ist ja nicht transparent. Was passiert muss, egal ob das nun das GUI-Toolkit für Dich machen würde oder Du das selber machst, ist den Ausschnitt vom Hintergrundbild nehmen der von der Schaltfläche verdeckt wird, da das Bild mit der Transparenz drauf klatschen, und das Ergebnis dann als Bild für die Schaltfläche verwenden. `PIL` hast du ja sowieso schon als Abhängigkeit, damit kann man diese Grafikoperationen durchführen. Das ganze kapselt man am besten in einer Klasse die eine Methode zum Setzen eines neuen Hintergrundbildes besitzt und sowohl das Bild im Hintergrund setzt als auch das Bild für die Schaltfläche neu berechnet und setzt.
M96Wayne
User
Beiträge: 22
Registriert: Freitag 20. Juni 2014, 18:18

Ich verstehe was du meinst, jedoch glaube ich,dass ich das mit meinem bisherigen Wissensstand nicht machen kann (ohne Hilfe).
BlackJack

Kleines Beispiel:

Code: Alles auswählen

try:
    import Tkinter as tk
except ImportError:
    import tkinter as tk
from itertools import cycle
from PIL import Image, ImageTk


class PleaseFindABetterNameForThisClass(tk.Frame):
    def __init__(self, master, image, button_image, command=None):
        tk.Frame.__init__(self, master)
        self.button_image = button_image
        self.background_photo = None
        self.background = tk.Label(self)
        self.background.pack()
        self.button_photo = None
        self.button = tk.Button(self, command=command)
        width, height = self.button_image.size
        self.button.place(x=0, y=0, width=width, height=height)
        self.set_background_image(image)


    def set_background_image(self, image):
        button_width, button_height = self.button_image.size
        button_image = image.crop((0, 0, button_width, button_height))
        button_image.paste(self.button_image, mask=self.button_image)
        self.button_photo = ImageTk.PhotoImage(button_image)
        self.background_photo = ImageTk.PhotoImage(image)
        self.button['image'] = self.button_photo
        self.background['image'] = self.background_photo


def main():
    background_filenames = cycle(['test.jpg', 'test2.jpg'])
    root = tk.Tk()
    widget = PleaseFindABetterNameForThisClass(
        root,
        Image.open(next(background_filenames)),
        Image.open('test.png'),
        lambda: widget.set_background_image(
            Image.open(next(background_filenames))
        )
    )
    widget.pack()
    root.mainloop()


if __name__ == '__main__':
    main()
M96Wayne
User
Beiträge: 22
Registriert: Freitag 20. Juni 2014, 18:18

Wow! Vielen Dank! Bin gerade dabei das Programm langsam zu durchblicken.
Danke für die Mühe! :D

Edit: Wie bekomm ich es denn, wenn ich meinen Button verschiebe (x-y Koordinaten verschieden wähle, dass sich die Schaltfläche dann auch verändert?

Code: Alles auswählen

button_image = image.crop((0, 0, button_width, button_height))
Ist das diese Zeile?
BlackJack

@M96Wayne: Ja, die Zeile müsste man auch entsprechend anpassen damit der richtige Bereich aus dem Hintergrundbild für die Schaltfläche gewählt wird.
M96Wayne
User
Beiträge: 22
Registriert: Freitag 20. Juni 2014, 18:18

Also wenn ich das so ändere (zB):


Code: Alles auswählen

self.button.place(x=100, y=100, width=width, height=height)
button_image = image.crop((100,100, button_width, button_height))
Kommt bei mir der Fehler:
ValueError: images do not match
BlackJack

@M96Wayne: Die beiden letzten Werte in dem Tupel bei `crop()` sind nicht die Breite und die Höhe des Ausschnitts sondern die Koordinate der rechten unteren Ecke. Breite und Höhe kann man da nur einsetzen wenn die linke obere Ecke die Koordinate 0, 0 hat.
M96Wayne
User
Beiträge: 22
Registriert: Freitag 20. Juni 2014, 18:18

Könntest du das mal mit den Werten (x=100,y=100) darstellen, weil ich kapier das nicht ganz (Sry bin noch Anfänger)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Die Überlegung ist doch eine ganz einfache: Wenn das Bild auf Position (0, 0) ist und die untere rechte Ecke sich bei (width, height) befindet, wo befindet sich dann die Ecke, wenn du das Bild auf die Position (x, y) verschiebst. Wenn du die Lösung nicht sofort siehst, dann male dir das ganze mal auf kariertem Papier auf.
Das Leben ist wie ein Tennisball.
BlackJack

@M96Wayne: Das Argument gibt die Koordinaten der beiden gegenüberliegenden Ecken eines Rechtecks an (x_1, y_1, x_2, y_2), wobei `x_1` und `y_1` die linke obere Ecke beschreiben und `x_2` und `y_2` die rechte untere Ecke. Wenn also das Bild auf der Schaltfläche 128 Pixel breit und hoch ist, dann schneidet man mit (0, 0, 128, 128) aus dem Hintergrundbild ganz oben links einen Bereich aus der 128 Pixel breit und hoch ist, und da wird dann das Bild für die Schaltfläche mit `paste()` drüber gelegt. Du hast jetzt aus den 0en jeweils eine 100 gemacht, das bedeutet also `crop()` wird (100, 100, 128, 128) übergeben, also ein viel kleineres Stück ausgeschnitten, das nur noch 28 Pixel breit und hoch ist, und da kann man kein 128×128 Pixel Bild drüber legen. Wenn Du beim Ausschneiden die linke obere Ecke verschiebst, dann musst Du das mit der rechten unteren ebenfalls machen, wenn der Ausschnitt gleich gross bleiben soll.
M96Wayne
User
Beiträge: 22
Registriert: Freitag 20. Juni 2014, 18:18

Ok! Habs jetzt endlich. Danke!
BlackJack

@M96Wayne: Da die 100 an zwei verschiedenen Stellen im Programm vorkommen sollte man die übrigens besser als Konstanten definieren oder den Benutzer der Klasse als Argumente (möglicherweise mit Default-Werten) übergeben lassen. Sonst muss man wenn man diesen Wert mal ändern will, daran denken auch alle Vorkommen zu verändern, statt nur an einer zentralen Stelle den Wert einmal zu ändern.
M96Wayne
User
Beiträge: 22
Registriert: Freitag 20. Juni 2014, 18:18

Wie kann ich das dann ändern würde es nämlich dann so machen:

Code: Alles auswählen

self.button.place(x=k, y=f, width=width, height=height)
button_image = image.crop((k, f, button_width+k,button_height+f))
Bloß weiß ich halt nich wie man das dann definiert bzw umsetzt, aber vom Prinzip her ist es ja richtig. Man müsste nur noch x bzw k irgendwo eingeben können
BlackJack

@M96Wayne: Man könnte die Werte wie gesagt als Argumente übergeben lassen wenn so ein `PleaseFindABetterNameForThisClass`-Exemplar erzeugt wird. Da die in der anderen Methode benötigt werden, gehören sie zum Zustand des Objekts, müssen also als Attribute gesetzt werden. Und auf jeden Fall unter besseren Namen als `k` und `f`.
Antworten